diff options
author | Willian Padovani Germano <wpgermano@gmail.com> | 2005-04-16 09:25:42 +0400 |
---|---|---|
committer | Willian Padovani Germano <wpgermano@gmail.com> | 2005-04-16 09:25:42 +0400 |
commit | d65fc84a6815673b9c4085bd4b3e4830167f59fb (patch) | |
tree | d8b083c5306ea01492f37fb92828fe4a0b42e1b4 /release/scripts | |
parent | 8b664b924b40aeddc8bc9ac72451a0702af75afe (diff) |
Note: this commit includes new functionality to save and restore scripts configure options. This is ongoing work, scripts still have to be updated to use this feature and more tests are needed, though many have been performed. The new Scripts Config Editor script is the main part of this. If anyone wants to check it, only the AC3D importer and exporter have already been updated to use it: simply open them (you can then cancel with ESC) to have the data created, then try the config editor.
Scripts:
- Thanks Jean-Michel Soler (jms) for updated versions of dispaint, fixfromarmature and unweld (also renamed to remove version part).
- Thanks Bart for the upgraded VRML exporter (great doc webpage!). It is available as VRML 97 and the original VRML 2 is for now still there, to help users testing the new version. For the next release the old one should be removed, of course.
- New script: Scripts Config Editor (Scripts win -> Scripts -> System). Scripts with config options (simple data that is to be set according to user needs or preferences) can use this facility instead of providing a gui and writing config files to disk themselves.
- Added new menu: System, available in the Scripts win.
- Updated sys_info.py, help_browse.py and the AC3D importer and exporter.
- Removed use of the Scrollbar and added arrow keys and mouse wheel support instead in Daniel Dunbar's old doc_browser.py. The scrollbar events handling doesn't exist, Ton suggested removing the scrollbar from the API months ago. For now its ref doc is gone and no bundled script uses it, until we get time to implement it properly.
- Added module BPyRegistry.py with functions to handle reading / writing config files automatically to the scripts/bpydata/config dir.
- Removing dir release/bpydata and its contents (moved earlier to release/scripts/bpydata/)
- Bug #2379: made small changes to bevel_center's ui to fix a problem reported by Alexander Ewering (intrr):
http://projects.blender.org/tracker/?func=detail&atid=125&aid=2379&group_id=9
BPython:
- Thanks Campbell Barton for new functionality: Blender.Get() now can also return all the paths from the user prefs -> file paths win and there is a new function: Blender.sys.expandpath() to transform Blender paths (those starting with '//' and ending with '#') to absolute paths.
- Added function Blender.ShowHelp(), to open the Scripts Help Browser with a given help page -- just a time saver for scripts.
- Improved function Blender.Run() to also work with gui and file select scripts.
- Found a (new?) crash related to NMesh.PutRaw when creating a new object while in edit mode. Leaving / entering edit mode fixes the problem, so a check for obj created, edit mode and leaving / re-entering it were added to the code for now (gdb didn't help much, no backtrace)
- doc updates, including splitting intro page in two, with bpython related stuff (registering / documenting / configuring scripts and command line mode (thanks Chris Want for "use system variables to pass parameters to scripts" idea).
- Registry: functions have been updated to support writing to / reading from disk, for the config editor -- only simple config data supported, for large amounts coders should write to a file themselves. This is done with a new parameter: Registry.GetKey(keyname, True) will also search for the key on the config dir, if not already loaded; equiv. for Registry.SetKey(keyname, dict, True). Data is only written to / read from disk when needed and only scripts already used (assuming they support this functionality) will have config data saved.
Diffstat (limited to 'release/scripts')
-rw-r--r-- | release/scripts/ac3d_export.py | 1008 | ||||
-rw-r--r-- | release/scripts/ac3d_import.py | 832 | ||||
-rw-r--r-- | release/scripts/bevel_center.py | 28 | ||||
-rw-r--r-- | release/scripts/bpymodules/BPyBlender.py | 2 | ||||
-rw-r--r-- | release/scripts/bpymodules/BPyRegistry.py | 248 | ||||
-rw-r--r-- | release/scripts/config.py | 792 | ||||
-rw-r--r-- | release/scripts/disp_paint.py | 16 | ||||
-rw-r--r-- | release/scripts/doc_browser.py | 45 | ||||
-rw-r--r-- | release/scripts/fixfromarmature.py | 31 | ||||
-rw-r--r-- | release/scripts/help_browser.py | 10 | ||||
-rw-r--r-- | release/scripts/sysinfo.py | 139 | ||||
-rw-r--r-- | release/scripts/unweld.py | 439 | ||||
-rw-r--r-- | release/scripts/vrml97_export.py | 1065 |
13 files changed, 3785 insertions, 870 deletions
diff --git a/release/scripts/ac3d_export.py b/release/scripts/ac3d_export.py index 2f44c0910c4..85d1ac5ceff 100644 --- a/release/scripts/ac3d_export.py +++ b/release/scripts/ac3d_export.py @@ -2,52 +2,75 @@ """ Registration info for Blender menus: Name: 'AC3D (.ac)...' -Blender: 233 +Blender: 236 Group: 'Export' -Submenu: 'All meshes...' all -Submenu: 'Only selected...' sel -Submenu: 'Configure +' config -Tip: 'Export to AC3D (.ac) format.' +Tip: 'Export selected meshes to AC3D (.ac) format' """ __author__ = "Willian P. Germano" __url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org", "PLib 3d gaming lib, http://plib.sf.net") -__version__ = "2.34 09/20/04" +__version__ = "2.36 2005-04-14" __bpydoc__ = """\ -This script exports Blender meshes to AC3D's .ac file format. +This script exports selected Blender meshes to AC3D's .ac file format. AC3D is a simple commercial 3d modeller also built with OpenGL. The .ac file format is an easy to parse text format well supported, -for example, by the PLib 3d gaming library (AC3D v3.x). +for example, by the PLib 3d gaming library (AC3D 3.x). Supported:<br> UV-textured meshes with hierarchy (grouping) information. Missing:<br> - Support for AC3D 4's crease tag (simple, will be added as option for -the next version of this exporter). + The 'url' tag, specific to AC3D. It is easy to add by hand to the exported +file, if needed. Known issues:<br> - Models textured with more than one image do not work -- for the -moment you can separate them in Blender such that each mesh only has one -image assigned (also see notes below);<br> - The exporter is slow for large meshes -- faster code was written for the -TuxKart (http://tuxkart.sf.net, wiki at http://netpanzer.berlios.de/tuxkart) -game exporter and will be integrated on a future version of this exporter. + The ambient and emit data we can retrieve from Blender are single values, +that this script copies to R, G, B, giving shades of gray.<br> + In AC3D 4 "compatibility mode":<br> + - shininess of materials is taken from the shader specularity value in Blender, mapped from [0.0, 2.0] to [0, 128];<br> + - crease angle is exported, but in Blender it is limited to [1, 80], since there are other more powerful ways to control surface smoothing. In AC3D 4.0 crease's range is [0.0, 180.0]; + +Config Options:<br> + toggle: + - AC3D 4 mode: unset it to export without the 'crease' tag that was +introduced with AC3D 4.0 and with the old material handling;<br> + - skip data: set it if you don't want mesh names (ME:, not OB: field) +to be exported as strings for AC's "data" tags (19 chars max);<br> + - rgb mirror color can be exported as ambient and/or emissive if needed, +since Blender handles these differently;<br> + - default mat: a default (white) material is added if some mesh was +left without mats -- it's better to always add your own materials;<br> + - no split: don't split meshes (see above);<br> + - set texture dir: override the actual textures path with a given default +path (or simply export the texture names, without dir info, if the path is +empty);<br> + strings: + - export dir: default dir to export to;<br> + - texture dir: override textures path with this path if 'set texture dir' +toggle is "on". Notes:<br> - There is a version of this script, by Ingo Ruhnke, that accepts meshes with -more than one texture image assigned, check TuxKart's wiki. + This version is considerably faster than previous ones for large meshes;<br> + Multiple textures per mesh are supported (mesh gets split);<br> + Parenting with meshes or empties as parents is converted to AC3D group +information;<br> + Start mesh object names (OB: field) with "!" or "#" if you don't want them to be exported;<br> + Start mesh object names (OB: field) with "=" or "$" to prevent them from being split (meshes with multiple textures or both textured and non textured faces are split unless this trick is used or the "no split" option is set. """ # $Id$ # # -------------------------------------------------------------------------- -# AC3DExport version 2.34 -# Program versions: Blender 2.34 and AC3Db files (means version 0xb) -# new: minor cosmetic tweaks, exporter itself didn't change +# AC3DExport version 2.36+ +# Program versions: Blender 2.36+ and AC3Db files (means version 0xb) +# new: faster, supports multiple textures per object and parenting is +# properly exported as group info, adapted to work with the Config Editor +# -------------------------------------------------------------------------- +# Thanks: Steve Baker for discussions and inspiration; for testing, bug +# reports, suggestions: David Megginson, Filippo di Natale, Franz Melchior # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # @@ -60,413 +83,602 @@ more than one texture image assigned, check TuxKart's wiki. # # 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 +# 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 sys as bsys +from Blender import Mathutils + +# Globals +ERROR_MSG = '' # popup error msg +REPORT_DATA = { + 'main': [], + 'errors': [], + 'warns': [], + 'nosplit': [], + 'noexport': [] +} +TOKENS_DONT_EXPORT = ['!', '#'] +TOKENS_DONT_SPLIT = ['=', '$'] +MATIDX_ERROR = False + +REG_KEY = 'ac3d_export' + +# config options: +SKIP_DATA = False +MIRCOL_AS_AMB = False +MIRCOL_AS_EMIS = False +ADD_DEFAULT_MAT = True +SET_TEX_DIR = True +TEX_DIR = '' +AC3D_4 = True # export crease value, compatible with AC3D 4 loaders +NO_SPLIT = False +EXPORT_DIR = '' + +tooltips = { + 'SKIP_DATA': "don't export mesh names as data fields", + 'MIRCOL_AS_AMB': "export mirror color as ambient color", + 'MIRCOL_AS_EMIS': "export mirror color as emissive color", + 'ADD_DEFAULT_MAT': "always add a default white material", + 'SET_TEX_DIR': "don't export default texture paths (edit also \"tex dir\")", + 'EXPORT_DIR': "default / last folder used to export .ac files to", + 'TEX_DIR': "(see \"set tex dir\") dir to prepend to all exported texture names (leave empty for no dir)", + 'AC3D_4': "compatibility mode, adds 'crease' tag and slightly better material support", + 'NO_SPLIT': "don't split meshes with multiple textures (or both textured and non textured polygons)", +} -ARG = __script__['arg'] # user selected argument +def update_RegistryInfo(): + d = {} + d['SKIP_DATA'] = SKIP_DATA + d['MIRCOL_AS_AMB'] = MIRCOL_AS_AMB + d['MIRCOL_AS_EMIS'] = MIRCOL_AS_EMIS + d['ADD_DEFAULT_MAT'] = ADD_DEFAULT_MAT + d['SET_TEX_DIR'] = SET_TEX_DIR + d['TEX_DIR'] = TEX_DIR + d['AC3D_4'] = AC3D_4 + d['NO_SPLIT'] = NO_SPLIT + d['EXPORT_DIR'] = EXPORT_DIR + d['tooltips'] = tooltips + Blender.Registry.SetKey(REG_KEY, d, True) -HELPME = 0 # help window +# Looking for a saved key in Blender.Registry dict: +rd = Blender.Registry.GetKey(REG_KEY, True) -SKIP_DATA = 1 -MIRCOL_AS_AMB = 0 -MIRCOL_AS_EMIS = 0 -ADD_DEFAULT_MAT = 1 +if rd: + try: + AC3D_4 = rd['AC3D_4'] + SKIP_DATA = rd['SKIP_DATA'] + MIRCOL_AS_AMB = rd['MIRCOL_AS_AMB'] + MIRCOL_AS_EMIS = rd['MIRCOL_AS_EMIS'] + ADD_DEFAULT_MAT = rd['ADD_DEFAULT_MAT'] + SET_TEX_DIR = rd['SET_TEX_DIR'] + TEX_DIR = rd['TEX_DIR'] + EXPORT_DIR = rd['EXPORT_DIR'] + NO_SPLIT = rd['NO_SPLIT'] + except KeyError: update_RegistryInfo() -# Looking for a saved key in Blender.Registry dict: -rd = Blender.Registry.GetKey('AC3DExport') +else: + update_RegistryInfo() + +VERBOSE = True +CONFIRM_OVERWRITE = True + +# check General scripts config key for default behaviors +rd = Blender.Registry.GetKey('General', True) if rd: - SKIP_DATA = rd['SKIP_DATA'] - MIRCOL_AS_AMB = rd['MIRCOL_AS_AMB'] - MIRCOL_AS_EMIS = rd['MIRCOL_AS_EMIS'] - ADD_DEFAULT_MAT = rd['ADD_DEFAULT_MAT'] + try: + VERBOSE = rd['verbose'] + CONFIRM_OVERWRITE = rd['confirm_overwrite'] + except: pass -def update_RegistryInfo(): - d = {} - d['SKIP_DATA'] = SKIP_DATA - d['MIRCOL_AS_AMB'] = MIRCOL_AS_AMB - d['MIRCOL_AS_EMIS'] = MIRCOL_AS_EMIS - d['ADD_DEFAULT_MAT'] = ADD_DEFAULT_MAT - Blender.Registry.SetKey('AC3DExport', d) # The default material to be used when necessary (see ADD_DEFAULT_MAT) DEFAULT_MAT = \ -'MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0.5 0.5 0.5 shi 64 trans 0' +'MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 \ +spec 0.5 0.5 0.5 shi 64 trans 0' # This transformation aligns Blender and AC3D coordinate systems: -acmatrix = [[1,0,0,0],[0,0,-1,0],[0,1,0,0],[0,0,0,1]] +acmatrix = Mathutils.Matrix([1,0,0,0], [0,0,-1,0], [0,1,0,0], [0,0,0,1]) def Round(f): - r = round(f,6) # precision set to 10e-06 - if r == int(r): - return str(int(r)) - else: - return str(r) - + r = round(f,6) # precision set to 10e-06 + if r == int(r): + return str(int(r)) + else: + return str(r) + def transform_verts(verts, m): - r = [] - for v in verts: - t = [0,0,0] - t[0] = m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0] - t[1] = m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1] - t[2] = m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2] - r.append(t) - return r - -def matrix_mul(m, n = acmatrix): - indices = [0,1,2,3] - t = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]] - for i in indices: - for j in indices: - for k in indices: - t[i][j] += m[i][k]*n[k][j] - return t + vecs = [] + for v in verts: + vec = Mathutils.Vector([v[0],v[1],v[2], 1]) + vecs.append(Mathutils.VecMultMat(vec, m)) + return vecs # --- -class AC3DExport: +# meshes with more than one texture assigned +# are split and saved as these foomeshes +class FooMesh: + + class FooVert: + def __init__(self, v): + self.v = v + self.index = 0 + + class FooFace: + def __init__(self, foomesh, f): + self.f = f + foov = foomesh.FooVert + self.v = [foov(f.v[0]), foov(f.v[1])] + len_fv = len(f.v) + if len_fv > 2 and f.v[2]: + self.v.append(foov(f.v[2])) + if len_fv > 3 and f.v[3]: self.v.append(foov(f.v[3])) + + def __getattr__(self, attr): + if attr == 'v': return self.v + return getattr(self.f, attr) + + def __len__(self): + return len(self.f) + + def __init__(self, tex, faces, mesh): + self.name = mesh.name + self.mesh = mesh + self.faces = [] + self.verts = verts = [] + vidxs = [0]*len(mesh.verts) + faces2 = [0]*len(faces) + for f in faces: + self.faces.append(self.FooFace(self, f)) + for v in f.v: + if v: vidxs[v.index] = 1 + i = 0 + for v in mesh.verts: + if vidxs[v.index]: + verts.append(v) + vidxs[v.index] = i + i += 1 + for f in self.faces: + for v in f.v: + if v: v.index = vidxs[v.v.index] + + def hasFaceUV(self): + return self.mesh.hasFaceUV() + + def getMaxSmoothAngle(self): + return self.mesh.getMaxSmoothAngle() + + +class AC3DExport: # the ac3d exporter part + + def __init__(self, scene_objects, filename): + + global ARG, SKIP_DATA, ADD_DEFAULT_MAT, DEFAULT_MAT + global ERROR_MSG, MATIDX_ERROR + + MATIDX_ERROR = 0 + + header = 'AC3Db' + self.buf = '' + self.mbuf = '' + self.mlist = [] + world_kids = 0 + kids_dict = self.kids_dict = {} + objs = [] + exp_objs = self.exp_objs = [] + tree = {} + + try: + file = self.file = open(filename, 'w') + except IOError, (errno, strerror): + error = "IOError #%s: %s" % (errno, strerror) + REPORT_DATA['errors'].append("Saving failed - %s." % error) + ERROR_MSG = "Couldn't save file!%%t|%s" % error + return None + + file.write(header+'\n') + + objs = \ + [o for o in scene_objects if o.getType() in ['Mesh', 'Empty']] + + for obj in objs[:]: + parent = obj.getParent() + list = [obj] + + while parent: + obj = parent + parent = parent.getParent() + list.insert(0, obj) + + dict = tree + for i in range(len(list)): + lname = list[i].getType()[:2] + list[i].name + if lname not in dict.keys(): + dict[lname] = {} + dict = dict[lname] + + self.traverse_dict(tree) + + world_kids = len(tree.keys()) + + objlist = [Blender.Object.Get(name) for name in exp_objs] + + meshlist = [o for o in objlist if o.getType() == 'Mesh'] + + self.MATERIALS(meshlist) + if not self.mbuf or ADD_DEFAULT_MAT: + self.mbuf = DEFAULT_MAT + '\n' + self.mbuf + file.write(self.mbuf) + + file.write('OBJECT world\nkids %s\n' % world_kids) + + for obj in objlist: + self.obj = obj + + objtype = obj.getType() + objname = obj.name + kidsnum = kids_dict[objname] + + if kidsnum: + self.OBJECT('group') + parent_is_mesh = 0 + if objtype == 'Mesh': + kidsnum += 1 + parent_is_mesh = 1 + self.name(objname) + self.kids(kidsnum) + + if objtype == 'Mesh': + mesh = self.mesh = obj.getData() + meshes = self.split_mesh(mesh) + if len(meshes) > 1: + if NO_SPLIT or self.dont_split(objname): + self.export_mesh(mesh, obj) + REPORT_DATA['nosplit'].append(objname) + else: + self.OBJECT('group') + self.name(objname) + self.kids(len(meshes)) + counter = 0 + for me in meshes: + self.export_mesh(me, obj, + name = '%s_%s' % (obj.name, counter), foomesh = True) + self.kids() + counter += 1 + else: + self.export_mesh(mesh, obj) + self.kids() + + file.close() + REPORT_DATA['main'].append("Done. Saved to: %s" % filename) + + def traverse_dict(self, dict): + kids_dict = self.kids_dict + exp_objs = self.exp_objs + keys = dict.keys() + for k in keys: + objname = k[2:] + klen = len(dict[k]) + kids_dict[objname] = klen + if self.dont_export(objname): + dict.pop(k) + parent = Blender.Object.Get(objname).getParent() + if parent: kids_dict[parent.name] -= 1 + REPORT_DATA['noexport'].append(objname) + continue + if klen: + self.traverse_dict(dict[k]) + exp_objs.insert(0, objname) + else: + if k.find('Em', 0) == 0: # Empty w/o children + dict.pop(k) + parent = Blender.Object.Get(objname).getParent() + if parent: kids_dict[parent.name] -= 1 + else: + exp_objs.insert(0, objname) + + def dont_export(self, name): # if name starts with '!' or '#' + length = len(name) + if length >= 1: + if name[0] in TOKENS_DONT_EXPORT: # '!' or '#' doubled (escaped): export + if length > 1 and name[1] == name[0]: + return 0 + return 1 + + def dont_split(self, name): # if name starts with '=' or '$' + length = len(name) + if length >= 1: + if name[0] in TOKENS_DONT_SPLIT: # '=' or '$' doubled (escaped): split + if length > 1 and name[1] == name[0]: + return 0 + return 1 + + def split_mesh(self, mesh): + tex_dict = {0:[]} + for f in mesh.faces: + if f.image: + if not f.image.name in tex_dict: tex_dict[f.image.name] = [] + tex_dict[f.image.name].append(f) + else: tex_dict[0].append(f) + keys = tex_dict.keys() + len_keys = len(keys) + if not tex_dict[0]: + len_keys -= 1 + tex_dict.pop(0) + keys.remove(0) + elif len_keys > 1: + lines = [] + anyimgkey = [k for k in keys if k != 0][0] + for f in tex_dict[0]: + if len(f.v) < 3: + lines.append(f) + if len(tex_dict[0]) == len(lines): + for l in lines: + tex_dict[anyimgkey].append(l) + len_keys -= 1 + tex_dict.pop(0) + if len_keys > 1: + foo_meshes = [] + for k in keys: + faces = tex_dict[k] + foo_meshes.append(FooMesh(k, faces, mesh)) + return foo_meshes + return [mesh] + + def export_mesh(self, mesh, obj, name = None, foomesh = False): + file = self.file + self.OBJECT('poly') + if not name: name = obj.name + self.name(name) + if not SKIP_DATA: + self.data(len(mesh.name), mesh.name) + texline = self.texture(mesh.faces) + if texline: file.write(texline) + if AC3D_4: + self.crease(mesh.getMaxSmoothAngle()) + self.numvert(mesh.verts, obj.getMatrix()) + self.numsurf(mesh.faces, mesh.hasFaceUV(), foomesh) + + def MATERIALS(self, meshlist): + for meobj in meshlist: + me = meobj.getData() + mat = me.materials + mbuf = [] + mlist = self.mlist + for m in xrange(len(mat)): + name = mat[m].name + try: + mlist.index(name) + except ValueError: + mlist.append(name) + M = Blender.Material.Get(name) + material = 'MATERIAL "%s"' % name + mirCol = "%s %s %s" % (Round(M.mirCol[0]), Round(M.mirCol[1]), + Round(M.mirCol[2])) + rgb = "rgb %s %s %s" % (Round(M.R), Round(M.G), Round(M.B)) + amb = "amb %s %s %s" % (Round(M.amb), Round(M.amb), Round(M.amb)) + spec = "spec %s %s %s" % (Round(M.specCol[0]), + Round(M.specCol[1]), Round(M.specCol[2])) + if AC3D_4: + emit = Round(M.emit) + emis = "emis %s %s %s" % (emit, emit, emit) + shival = int(M.spec * 64) + else: + emis = "emis 0 0 0" + shival = 72 + shi = "shi %s" % shival + trans = "trans %s" % (Round(1 - M.alpha)) + if MIRCOL_AS_AMB: + amb = "amb %s" % mirCol + if MIRCOL_AS_EMIS: + emis = "emis %s" % mirCol + mbuf.append("%s %s %s %s %s %s %s\n" \ + % (material, rgb, amb, emis, spec, shi, trans)) + self.mlist = mlist + self.mbuf += "".join(mbuf) + + def OBJECT(self, type): + self.file.write('OBJECT %s\n' % type) + + def name(self, name): + if name[0] in TOKENS_DONT_EXPORT or name[0] in TOKENS_DONT_SPLIT: + if len(name) > 1: name = name[1:] + self.file.write('name "%s"\n' % name) + + def kids(self, num = 0): + self.file.write('kids %s\n' % num) + + def data(self, num, str): + self.file.write('data %s\n%s\n' % (num, str)) + + def texture(self, faces): + tex = "" + for f in faces: + if f.image: + tex = f.image.name + break + if tex: + image = Blender.Image.Get(tex) + texfname = image.filename + if SET_TEX_DIR: + texfname = Blender.sys.basename(texfname) + if TEX_DIR: + texfname = Blender.sys.join(TEX_DIR, texfname) + buf = 'texture "%s"\n' % texfname + xrep = image.xrep + yrep = image.yrep + buf += 'texrep %s %s\n' % (xrep, yrep) + self.file.write(buf) + + def rot(self, matrix): + rot = '' + not_I = 0 + for i in [0, 1, 2]: + r = map(Round, matrix[i]) + not_I += (r[0] != '0.0')+(r[1] != '0.0')+(r[2] != '0.0') + not_I -= (r[i] == '1.0') + for j in [0, 1, 2]: + rot = "%s %s" % (rot, r[j]) + if not_I: + self.file.write('rot %s\n' % rot.strip()) + + def loc(self, loc): + loc = map(Round, loc) + if loc[0] or loc[1] or loc[2]: + self.file.write('loc %s %s %s\n' % (loc[0], loc[1], loc[2])) + + def crease(self, crease): + self.file.write('crease %s\n' % crease) + + def numvert(self, verts, matrix): + file = self.file + file.write("numvert %s\n" % len(verts)) + m = matrix * acmatrix + verts = transform_verts(verts, m) + for v in verts: + v0, v1, v2 = Round(v[0]), Round(v[1]), Round(v[2]) + file.write("%s %s %s\n" % (v0, v1, v2)) + + def numsurf(self, faces, hasFaceUV, foomesh = False): + + global ADD_DEFAULT_MAT, MATIDX_ERROR + file = self.file + + file.write("numsurf %s\n" % len(faces)) + + if not foomesh: verts = self.mesh.verts + + mlist = self.mlist + omlist = {} + objmats = self.mesh.materials[:] + matidx_error_told = 0 + for i in range(len(objmats)): + objmats[i] = objmats[i].name + for f in faces: + m_idx = f.materialIndex + try: + m_idx = mlist.index(objmats[m_idx]) + except IndexError: + if not MATIDX_ERROR: + rdat = REPORT_DATA['warns'] + rdat.append("Object %s" % self.obj.name) + rdat.append("has at least one material *index* assigned but not") + rdat.append("defined (not linked to an existing material).") + rdat.append("Result: some faces may be exported with a wrong color.") + rdat.append("You can link materials in the Edit Buttons window (F9).") + elif not matidx_error_told: + midxmsg = "- Same for object %s." % self.obj.name + REPORT_DATA['warns'].append(midxmsg) + MATIDX_ERROR += 1 + matidx_error_told = 1 + m_idx = 0 + refs = len(f) + flaglow = (refs == 2) << 1 + two_side = f.mode & Blender.NMesh.FaceModes['TWOSIDE'] + two_side = (two_side > 0) << 1 + flaghigh = f.smooth | two_side + surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow) + if ADD_DEFAULT_MAT and objmats: m_idx += 1 + matstr = "mat %s\n" % m_idx + refstr = "refs %s\n" % refs + u, v, vi = 0, 0, 0 + fvstr = [] + if foomesh: + for vert in f.v: + fvstr.append(str(vert.index)) + if hasFaceUV: + u = f.uv[vi][0] + v = f.uv[vi][1] + vi += 1 + fvstr.append(" %s %s\n" % (u, v)) + else: + for vert in f.v: + fvstr.append(str(verts.index(vert))) + if hasFaceUV: + u = f.uv[vi][0] + v = f.uv[vi][1] + vi += 1 + fvstr.append(" %s %s\n" % (u, v)) + + fvstr = "".join(fvstr) + + file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr)) - def __init__(self, scene, filename): +# End of Class AC3DExport - global ARG, SKIP_DATA, ADD_DEFAULT_MAT, DEFAULT_MAT +from Blender.Window import FileSelector + +def report_data(): + global VERBOSE + + if not VERBOSE: return + + d = REPORT_DATA + msgs = { + '0main': '%s\nExporting meshes to AC3D format' % str(19*'-'), + '1warns': 'Warnings', + '2errors': 'Errors', + '3nosplit': 'Not split (because name starts with "=" or "$")', + '4noexport': 'Not exported (because name starts with "!" or "#")' + } + if NO_SPLIT: + l = msgs['3nosplit'] + l = "%s (because OPTION NO_SPLIT is set)" % l.split('(')[0] + msgs['3nosplit'] = l + keys = msgs.keys() + keys.sort() + for k in keys: + msgk = msgs[k] + msg = '\n'.join(d[k[1:]]) + if msg: + print '\n-%s:' % msgk + print msg + +# File Selector callback: +def fs_callback(filename): + global ERROR_MSG, EXPORT_DIR, OBJS, CONFIRM_OVERWRITE, VERBOSE - print 'Trying AC3DExport...' + if not filename.endswith('.ac'): filename = '%s.ac' % filename - header = 'AC3Db' - self.buf = '' - self.mbuf = '' - world_kids = 0 - self.mlist = [] - kids_dict = {} - objlist = [] - bl_objlist2 = [] - - if ARG == 'all': bl_objlist = scene.getChildren() - elif ARG == 'sel': bl_objlist = Blender.Object.GetSelected() - - for obj in bl_objlist: - if obj.getType() != 'Mesh' and obj.getType() != 'Empty': - continue - else: kids_dict[obj.name] = 0 - if obj.getParent() == None: - objlist.append(obj.name) - else: - bl_objlist2.append(obj) - - bl_objlist = bl_objlist2[:] - world_kids = len(objlist) - - while bl_objlist2: - for obj in bl_objlist: - obj2 = obj - dad = obj.getParent() - kids_dict[dad.name] += 1 - while dad.name not in objlist: - obj2 = dad - dad = dad.getParent() - kids_dict[dad.name] += 1 - objlist.insert(objlist.index(dad.name)+1, obj2.name) - bl_objlist2.remove(obj2) - - for object in objlist: - obj = Blender.Object.Get(object) - self.obj = obj - - if obj.getType() == 'Empty': - self.OBJECT("group") - self.name(obj.name) - #self.rot(obj.rot) - #self.loc(obj.loc) - else: - mesh = self.mesh = obj.getData() - self.MATERIAL(mesh.materials) - self.OBJECT("poly") - self.name(obj.name) - if not SKIP_DATA: self.data(mesh.name) - self.texture(mesh.faces) - self.numvert(mesh.verts, obj.getMatrix()) - self.numsurf(mesh.faces, mesh.hasFaceUV()) - - self.kids(kids_dict[object]) - - if not self.mbuf or ADD_DEFAULT_MAT: - self.mbuf = DEFAULT_MAT + '\n' + self.mbuf - print "\nNo materials: a default (white) has been assigned.\n" - self.mbuf = self.mbuf + "%s\n%s %s\n" \ - % ('OBJECT world', 'kids', world_kids) - buf = "%s\n%s%s" % (header, self.mbuf, self.buf) - - if filename.find('.ac', -3) <= 0: filename += '.ac' - - try: - file = open(filename, 'w') - except IOError, (errno, strerror): - errmsg = "IOError #%s" % errno - errmsg = errmsg + "%t|" + strerror - Blender.Draw.PupMenu(errmsg) - return None - file.write(buf) - file.close() - - print "Done. Saved to %s\n" % filename - - def MATERIAL(self, mat): - if mat == [None]: - print "Notice -- object %s has no material linked to it:" % self.name - print "\tThe first entry in the .ac file will be used." - return - mbuf = '' - mlist = self.mlist - for m in xrange(len(mat)): - name = mat[m].name - try: - mlist.index(name) - except ValueError: - mlist.append(name) - M = Blender.Material.Get(name) - material = 'MATERIAL "%s"' % name - mirCol = "%s %s %s" % (Round(M.mirCol[0]), - Round(M.mirCol[1]), Round(M.mirCol[2])) - rgb = "rgb %s %s %s" % (Round(M.R), Round(M.G), Round(M.B)) - amb = "amb %s %s %s" % (Round(M.amb), Round(M.amb), Round(M.amb)) - if MIRCOL_AS_AMB: - amb = "amb %s" % mirCol - emis = "emis 0 0 0" - if MIRCOL_AS_EMIS: - emis = "emis %s" % mirCol - spec = "spec %s %s %s" % (Round(M.specCol[0]), - Round(M.specCol[1]), Round(M.specCol[2])) - shi = "shi 72" - trans = "trans %s" % (Round(1 - M.alpha)) - mbuf = mbuf + "%s %s %s %s %s %s %s\n" \ - % (material, rgb, amb, emis, spec, shi, trans) - self.mlist = mlist - self.mbuf = self.mbuf + mbuf - - def OBJECT(self, type): - self.buf = self.buf + "OBJECT %s\n" % type - - def name(self, name): - self.buf = self.buf + 'name "%s"\n' % name - - def data(self, name): - self.buf = self.buf + 'data %s\n%s\n' % (len(name), name) - - def texture(self, faces): - tex = [] - for f in faces: - if f.image and f.image.name not in tex: - tex.append(f.image.name) - if tex: - if len(tex) > 1: - print "\nAC3Db format supports only one texture per object." - print "Object %s -- using only the first one: %s\n" % (self.obj.name, tex[0]) - image = Blender.Image.Get(tex[0]) - buf = 'texture "%s"\n' % image.filename - xrep = image.xrep - yrep = image.yrep - buf += 'texrep %s %s\n' % (xrep, yrep) - self.buf = self.buf + buf - - def rot(self, matrix): - rot = '' - not_I = 0 - for i in [0, 1, 2]: - r = map(Round, matrix[i]) - not_I += (r[0] != '0.0')+(r[1] != '0.0')+(r[2] != '0.0') - not_I -= (r[i] == '1.0') - for j in [0, 1, 2]: - rot = "%s %s" % (rot, r[j]) - if not_I: - rot = rot.strip() - buf = 'rot %s\n' % rot - self.buf = self.buf + buf - - def loc(self, loc): - loc = map(Round, loc) - if loc[0] or loc[1] or loc[2]: - buf = 'loc %s %s %s\n' % (loc[0], loc[1], loc[2]) - self.buf = self.buf + buf - - def numvert(self, verts, matrix): - buf = "numvert %s\n" % len(verts) - m = matrix_mul(matrix) - verts = transform_verts(verts, m) - for v in verts: - v = map(Round, v) - buf = buf + "%s %s %s\n" % (v[0], v[1], v[2]) - self.buf = self.buf + buf - - def numsurf(self, faces, hasFaceUV): - - global ADD_DEFAULT_MAT - - buf = "numsurf %s\n" % len(faces) - - mlist = self.mlist - indexerror = 0 - omlist = {} - objmats = self.mesh.materials - for i in range(len(objmats)): - objmats[i] = objmats[i].name - for f in faces: - m_idx = f.materialIndex - try: - m_idx = mlist.index(objmats[m_idx]) - except IndexError: - if not indexerror: - print "\nNotice: object " + self.obj.name + \ - " has at least one material *index* assigned" - print "\tbut not defined (not linked to an existing material)." - print "\tThis can cause some of its faces to be exported with a wrong color." - print "\tYou can fix the problem in the Blender Edit Buttons Window (F9).\n" - indexerror = 1 - m_idx = 0 - refs = len(f) - flaglow = (refs == 2) << 1 - two_side = f.mode & Blender.NMesh.FaceModes['TWOSIDE'] - two_side = (two_side > 0) << 1 - flaghigh = f.smooth | two_side - buf = buf + "SURF 0x%d%d\n" % (flaghigh, flaglow) - if ADD_DEFAULT_MAT and objmats: m_idx += 1 - buf = buf + "mat %s\n" % m_idx - buf = buf + "refs %s\n" % refs - u, v, vi = 0, 0, 0 - for vert in f.v: - vindex = self.mesh.verts.index(vert) - if hasFaceUV: - u = f.uv[vi][0] - v = f.uv[vi][1] - vi += 1 - buf = buf + "%s %s %s\n" % (vindex, u, v) - self.buf = self.buf + buf - - def kids(self, kids = 0): - self.buf = self.buf + "kids %s\n" % kids + if bsys.exists(filename) and CONFIRM_OVERWRITE: + if Blender.Draw.PupMenu('OVERWRITE?%t|File exists') != 1: + return -# End of Class AC3DExport + Blender.Window.WaitCursor(1) + starttime = bsys.time() -from Blender import Draw, BGL - -def gui(): - global SKIP_DATA, MIRCOL_AS_AMB, MIRCOL_AS_EMIS, ADD_DEFAULT_MAT, HELPME - global HELPME - - if HELPME: - BGL.glClearColor(0.6,0.6,0.9,1) - BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) - BGL.glColor3f(1,1,1) - BGL.glRasterPos2i(18, 150) - Draw.Text("AC3D is a simple, affordable commercial 3d modeller that can " - "be found at www.ac3d.org .") - BGL.glRasterPos2i(18, 130) - Draw.Text("It uses a nice text file format (extension .ac) which supports " - "uv-textured meshes") - BGL.glRasterPos2i(18, 110) - Draw.Text("with parenting (grouping) information.") - BGL.glRasterPos2i(18, 90) - Draw.Text("Notes: AC3D has a 'data' token that assigns a string to each " - "mesh, useful for games,") - BGL.glRasterPos2i(55, 70) - Draw.Text("for example. You can use Blender's mesh 'ME:' field for that.") - BGL.glRasterPos2i(55, 50) - Draw.Text("The .ac format is well supported by the PLib 3d gaming library.") - Draw.Button("Ok", 21, 285, 10, 45, 20, - "Click to return to previous screen.") - else: - BGL.glClearColor(0,0,1,1) - BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) - BGL.glColor3f(1,1,1) - BGL.glRasterPos2i(20, 150) - Draw.Text("AC3D Exporter") - Draw.Toggle("Default mat", 1, 15, 100, 90, 20, ADD_DEFAULT_MAT, - "Objects without materials assigned get a default (white) one" - " automatically.") - Draw.Toggle("Skip data", 2, 15, 80, 90, 20, SKIP_DATA, - "Don't export mesh names as 'data' info.") - Draw.Toggle("Mir2Amb", 3, 15, 50, 90, 20, MIRCOL_AS_AMB, - "Get AC3D's ambient RGB color for each object from its mirror color " - "in Blender.") - Draw.Toggle("Mir2Emis", 4, 15, 30, 90, 20, MIRCOL_AS_EMIS, - "Get AC3D's emissive RGB color for each object from its mirror color " - "in Blender.") - Draw.Button("Export All...", 10, 140, 80, 110, 30, - "Export all meshes to an AC3D file.") - Draw.Button("Export Selected...", 11, 140, 40, 110, 30, - "Export selected meshes to an AC3D file.") - Draw.Button("HELP", 20, 285, 80, 100, 40, "Click for additional info.") - Draw.Button("EXIT", 22, 285, 30, 100, 40, "Click to leave.") - -def event(evt, val): - global HELPME - - if not val: return - - if HELPME: - if evt == Draw.ESCKEY: - HELPME = 0 - Draw.Register(gui, event, b_event) - return - else: return - - if evt == Draw.ESCKEY: - update_RegistryInfo() - Draw.Exit() - return - else: return - - Draw.Register(gui, event, b_event) - -def b_event(evt): - global ARG, SKIP_DATA, MIRCOL_AS_AMB, MIRCOL_AS_EMIS, ADD_DEFAULT_MAT - global HELPME - - if evt == 1: - ADD_DEFAULT_MAT = 1 - ADD_DEFAULT_MAT - Draw.Redraw(1) - elif evt == 2: - SKIP_DATA = 1 - SKIP_DATA - Draw.Redraw(1) - elif evt == 3: - MIRCOL_AS_AMB = 1 - MIRCOL_AS_AMB - Draw.Redraw(1) - elif evt == 4: - MIRCOL_AS_EMIS = 1 - MIRCOL_AS_EMIS - Draw.Redraw(1) - elif evt == 10: - ARG = 'all' - fname = Blender.sys.makename(ext=".ac") - Blender.Window.FileSelector(fs_callback, "Export AC3D", fname) - elif evt == 11: - ARG = 'sel' - fname = Blender.sys.makename(ext=".ac") - Blender.Window.FileSelector(fs_callback, "Export AC3D", fname) - elif evt == 20: - HELPME = 1 - HELPME - Draw.Redraw(1) - elif evt == 21: # leave Help screen - HELPME = 0 - Draw.Register(gui, event, b_event) - elif evt == 22: - update_RegistryInfo() - Draw.Exit() - else: - Draw.Register(gui, event, b_event) + export_dir = bsys.dirname(filename) + if export_dir != EXPORT_DIR: + EXPORT_DIR = export_dir + update_RegistryInfo() -def fs_callback(filename): - scene = Blender.Scene.GetCurrent() - test = AC3DExport(scene, filename) + test = AC3DExport(OBJS, filename) + if ERROR_MSG: + Blender.Draw.PupMenu(ERROR_MSG) + ERROR_MSG = '' + else: + endtime = bsys.time() - starttime + REPORT_DATA['main'].append("Data exported in %.3f seconds." % endtime) + + if VERBOSE: report_data() + Blender.Window.WaitCursor(0) + return + + +# -- End of definitions + +OBJS = Blender.Object.GetSelected() -if __script__['arg'] == 'config': - Draw.Register(gui, event, b_event) +if not OBJS: + Blender.Draw.PupMenu('ERROR: No objects selected') else: - fname = Blender.sys.makename(ext=".ac") - Blender.Window.FileSelector(fs_callback, "Export AC3D", fname) + fname = bsys.makename(ext=".ac") + if EXPORT_DIR: + fname = bsys.join(EXPORT_DIR, bsys.basename(fname)) + FileSelector(fs_callback, "Export AC3D", fname) diff --git a/release/scripts/ac3d_import.py b/release/scripts/ac3d_import.py index 07e8540798a..d2505022adf 100644 --- a/release/scripts/ac3d_import.py +++ b/release/scripts/ac3d_import.py @@ -2,7 +2,7 @@ """ Registration info for Blender menus: Name: 'AC3D (.ac)...' -Blender: 232 +Blender: 236 Group: 'Import' Tip: 'Import an AC3D (.ac) file.' """ @@ -10,40 +10,46 @@ Tip: 'Import an AC3D (.ac) file.' __author__ = "Willian P. Germano" __url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org", "PLib 3d gaming lib, http://plib.sf.net") -__version__ = "2.34 07/26/04" +__version__ = "2.36 2005-04-14" __bpydoc__ = """\ This script imports AC3D models into Blender. AC3D is a simple and affordable commercial 3d modeller also built with OpenGL. The .ac file format is an easy to parse text format well supported, -for example, by the PLib 3d gaming library (AC3D v3.x). +for example, by the PLib 3d gaming library. Supported:<br> UV-textured meshes with hierarchy (grouping) information. Missing:<br> - Support for AC3D 4's crease tag (simple, will be added soon). + The url tag is irrelevant for Blender. Known issues:<br> None. +Config Options:<br> + - group (toggle): if "on", grouped objects in the .ac file are parented to +Empties. + - textures dir (string): if non blank, when imported texture paths are +wrong in the .ac file, Blender will also look for them at this dir. + Notes:<br> - Check script's source code for options that can be tweaked (using Blender -empties to emulate AC3D's grouping info and setting a default folder for -textures, for the case of wrong paths in the .ac file(s). + - when looking for assigned textures, Blender tries in order: the actual +paths from the .ac file, the .ac file's dir and the default textures dir path +users can configure (see config options above). """ # $Id$ # # -------------------------------------------------------------------------- -# AC3DImport version 2.34 Jul 26, 2004 -# Program versions: Blender 2.32+ and AC3Db files (means version 0xb) -# small update to allow a default path for textures, see TEXDIR below. +# AC3DImport version 2.36 Apr 14, 2005 +# Program versions: Blender 2.36+ and AC3Db files (means version 0xb) +# changed: updated to use the Scripts Config Editor facilities # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # -# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br +# Copyright (C) 2005: Willian P. Germano, wgermano _at_ ig.com.br # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -52,7 +58,7 @@ textures, for the case of wrong paths in the .ac file(s). # # 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 +# 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 @@ -64,394 +70,454 @@ textures, for the case of wrong paths in the .ac file(s). # Note: # Blender doesn't handle n-gons (polygons with more than 4 vertices): -# The script triangulates them, but concave polygons come out wrong and need -# fixing. Avoiding or triangulating concave n-gons in AC3D is a simple way to -# avoid problems. +# The script triangulates them, but concave polygons come out wrong and need +# fixing. Avoiding or triangulating concave n-gons in AC3D is a simple way to +# avoid problems. + +import Blender +from Blender import Registry +from Blender.sys import dirsep # Default folder for AC3D textures, to override wrong paths, change to your # liking or leave as "": -TEXDIR = "" +TEXTURES_DIR = "" -# Set 'GROUP' to 1 to make Blender group imported objects using Empties, +# Set 'GROUP' to True to make Blender group imported objects using Empties, # to reproduce the object hierarchy in the .ac file -GROUP = 0 +GROUP = False -import Blender +tooltips = { + 'TEXTURES_DIR': 'additional dir to look for missing textures', + 'GROUP': 'mimick grouping information by parenting grouped meshes to empties' +} + +def update_registry(): + global GROUP, TEXTURES_DIR + rd = dict([('GROUP', GROUP), ('TEXTURES_DIR', TEXTURES_DIR)]) + Registry.SetKey('ac3d_import', rd, True) + +rd = Registry.GetKey('ac3d_import', True) + +if rd: + TEXTURES_DIR = rd['TEXTURES_DIR'] + GROUP = rd['GROUP'] +else: update_registry() -if TEXDIR: - TEXDIR = TEXDIR.replace('\\','/') - if TEXDIR[-1] != '/': TEXDIR += '/' +if TEXTURES_DIR: + oldtexdir = TEXTURES_DIR + if dirsep == '/': TEXTURES_DIR = TEXTURES_DIR.replace('\\', '/') + if TEXTURES_DIR[-1] != dirsep: TEXTURES_DIR = "%s%s" % (TEXTURES_DIR, dirsep) + if oldtexdir != TEXTURES_DIR: update_registry() + +VERBOSE = True +rd = Registry.GetKey('General', True) +if rd: + if rd.has_key('verbose'): + VERBOSE = rd['verbose'] + + errmsg = "" + +def inform(msg): + global VERBOSE + if VERBOSE: print msg + + class Obj: - - def __init__(self, type): - self.type = type - self.dad = None - self.name = '' - self.data = '' - self.tex = '' - self.texrep = [1,1] - self.texoff = None - self.loc = [0, 0, 0] - self.rot = [] - self.vlist = [] - self.flist = [] - self.matlist = [] - self.kids = 0 + + def __init__(self, type): + self.type = type + self.dad = None + self.name = '' + self.data = '' + self.tex = '' + self.texrep = [1,1] + self.texoff = None + self.loc = [0, 0, 0] + self.rot = [] + self.crease = 30 + self.vlist = [] + self.flist = [] + self.matlist = [] + self.kids = 0 class AC3DImport: - def __init__(self, filename): - - global errmsg - - print "Trying to import AC3D model(s) from %s ..." % filename - - self.i = 0 - errmsg = '' - self.importdir = Blender.sys.dirname(filename) - try: - file = open(filename, 'r') - except IOError, (errno, strerror): - errmsg = "IOError #%s: %s" % (errno, strerror) - print errmsg - return None - header = file.read(5) - header, version = header[:4], header[-1] - if header != 'AC3D': - file.close() - errmsg = 'Invalid file -- AC3D header not found.' - print errmsg - return None - elif version != 'b': - print 'AC3D file version 0x%s.' % version - print 'This importer is for version 0xb, so it may fail.' - - self.token = {'OBJECT': self.parse_obj, - 'numvert': self.parse_vert, - 'numsurf': self.parse_surf, - 'name': self.parse_name, - 'data': self.parse_data, - 'kids': self.parse_kids, - 'loc': self.parse_loc, - 'rot': self.parse_rot, - 'MATERIAL': self.parse_mat, - 'texture': self.parse_tex, - 'texrep': self.parse_texrep, - 'texoff': self.parse_texoff} - - self.objlist = [] - self.mlist = [] - self.dads = [] - self.kids = [] - self.dad = None - - self.lines = file.readlines() - self.lines.append('') - self.parse_file() - file.close() - - self.testAC3DImport() - - def parse_obj(self, value): - if self.kids: - while not self.kids[-1]: - self.kids.pop() - self.dad = self.dad.dad - self.kids[-1] -= 1 - new = Obj(value) - new.dad = self.dad - new.name = value - self.objlist.append(new) - - def parse_kids(self, value): - kids = int(value) - if kids: - self.kids.append(kids) - self.dad = self.objlist[-1] - self.objlist[-1].kids = kids - - def parse_name(self, value): - name = value.split('"')[1] - self.objlist[-1].name = name - - def parse_data(self, value): - data = self.lines[self.i].strip() - self.objlist[-1].data = data - - def parse_tex(self, value): - texture = value.split('"')[1] - self.objlist[-1].tex = texture - - def parse_texrep(self, trash): - trep = self.lines[self.i - 1] - trep = trep.split() - trep = [float(trep[1]), float(trep[2])] - self.objlist[-1].texrep = trep - self.objlist[-1].texoff = [0, 0] - - def parse_texoff(self, trash): - toff = self.lines[self.i - 1] - toff = toff.split() - toff = [float(toff[1]), float(toff[2])] - self.objlist[-1].texoff = toff - - def parse_mat(self, value): - i = self.i - 1 - lines = self.lines - line = lines[i].split() - mat_name = '' - mat_col = mat_spec_col = [0,0,0] - mat_alpha = 1 - - while line[0] == 'MATERIAL': - mat_name = line[1].split('"')[1] - mat_col = map(float,[line[3],line[4],line[5]]) - mat_spec_col = map(float,[line[15],line[16],line[17]]) - mat_alpha = float(line[-1]) - mat_alpha = 1 - mat_alpha - self.mlist.append([mat_name, mat_col, mat_spec_col, mat_alpha]) - i += 1 - line = lines[i].split() - - self.i = i - - def parse_rot(self, trash): - i = self.i - 1 - rot = self.lines[i].split(' ', 1)[1] - rot = map(float, rot.split()) - self.objlist[-1].rot = rot - - def parse_loc(self, trash): - i = self.i - 1 - loc = self.lines[i].split(' ', 1)[1] - loc = map(float, loc.split()) - self.objlist[-1].loc = loc - - def parse_vert(self, value): - i = self.i - lines = self.lines - obj = self.objlist[-1] - vlist = obj.vlist - n = int(value) - - while n: - line = lines[i].split() - line = map(float, line) - vlist.append(line) - n -= 1 - i += 1 - - self.i = i - - rot = obj.rot - if rot: - nv = len(vlist) - for j in range(nv): - v = vlist[j] - t = [0,0,0] - t[0] = rot[0]*v[0] + rot[3]*v[1] + rot[6]*v[2] - t[1] = rot[1]*v[0] + rot[4]*v[1] + rot[7]*v[2] - t[2] = rot[2]*v[0] + rot[5]*v[1] + rot[8]*v[2] - vlist[j] = t - - loc = obj.loc - dad = obj.dad - while dad: - for j in [0, 1, 2]: - loc[j] += dad.loc[j] - dad = dad.dad - - for v in vlist: - for j in [0, 1, 2]: - v[j] += loc[j] - - def parse_surf(self, value): - i = self.i - is_smooth = 0 - double_sided = 0 - lines = self.lines - obj = self.objlist[-1] - matlist = obj.matlist - numsurf = int(value) - - while numsurf: - flags = lines[i].split() - flaglow = 0 - if len(flags[1]) > 3: flaglow = int(flags[1][3]) - flaghigh = int(flags[1][2]) - is_smooth = flaghigh & 1 - twoside = flaghigh & 2 - mat = lines[i+1].split() - mat = int(mat[1]) - if not mat in matlist: matlist.append(mat) - refs = lines[i+2].split() - refs = int(refs[1]) - i += 3 - face = [] - faces = [] - fuv = [] - rfs = refs - - while rfs: - line = lines[i].split() - v = int(line[0]) - uv = [float(line[1]), float(line[2])] - face.append([v, uv]) - rfs -= 1 - i += 1 - - if flaglow: - while len(face) >= 2: - cut = face[:2] - faces.append(cut) - face = face[1:] - - if flaglow == 1: - face = [faces[-1][-1], faces[0][0]] - faces.append(face) - - else: - while len(face) > 4: - cut = face[:4] - face = face[3:] - face.insert(0, cut[0]) - faces.append(cut) - - faces.append(face) - - for f in faces: - f.append(mat) - f.append(is_smooth) - f.append(twoside) - self.objlist[-1].flist.append(f) - - numsurf -= 1 - - - self.i = i - - def parse_file(self): - i = 1 - lines = self.lines - line = lines[i].split() - - while line: - kw = '' - for k in self.token.keys(): - if line[0] == k: - kw = k - break - i += 1 - if kw: - self.i = i - self.token[kw](line[1]) - i = self.i - line = lines[i].split() - - def testAC3DImport(self): - global GROUP - scene = Blender.Scene.GetCurrent() - - bmat = [] - for mat in self.mlist: - name = mat[0] - m = Blender.Material.New(name) - m.rgbCol = (mat[1][0], mat[1][1], mat[1][2]) - m.specCol = (mat[2][0], mat[2][1], mat[2][2]) - m.alpha = mat[3] - bmat.append(m) - - for obj in self.objlist: - if obj.type == 'world': - continue - elif obj.type == 'group': - if not GROUP: continue - empty = Blender.Object.New('Empty') - empty.name = obj.name - scene.link(empty) - if self.dads: - dadobj = Blender.Object.get(self.dads.pop()) - dadobj.makeParent([empty]) - while obj.kids: - self.dads.append(empty.name) - obj.kids -= 1 - continue - mesh = Blender.NMesh.New() - if (obj.data): mesh.name = obj.data - mesh.hasFaceUV(1) - - tex = None - if obj.tex != '': - try: - tex = Blender.Image.Load(obj.tex) - # Commented because it's unnecessary: - #tex.xrep = int(obj.texrep[0]) - #tex.yrep = int(obj.texrep[1]) - except: - basetexname = Blender.sys.basename(obj.tex) - try: - obj.tex = self.importdir + '/' + basetexname - tex = Blender.Image.Load(obj.tex) - except: - try: - obj.tex = TEXDIR + basetexname - tex = Blender.Image.Load(obj.tex) - except: - print "Couldn't load texture: %s" % basetexname - - for v in obj.vlist: - bvert = Blender.NMesh.Vert(v[0],v[1],v[2]) - mesh.verts.append(bvert) - - objmat_indices = [] - for mat in bmat: - if bmat.index(mat) in obj.matlist: - objmat_indices.append(bmat.index(mat)) - mesh.materials.append(mat) - for f in obj.flist: - twoside = f[-1] - is_smooth = f[-2] - fmat = f[-3] - f=f[:-3] - bface = Blender.NMesh.Face() - bface.smooth = is_smooth - if twoside: bface.mode |= Blender.NMesh.FaceModes['TWOSIDE'] - if tex: - bface.mode |= Blender.NMesh.FaceModes['TEX'] - bface.image = tex - bface.materialIndex = objmat_indices.index(fmat) - if obj.texoff: - uoff = obj.texoff[0] - voff = obj.texoff[1] - urep = obj.texrep[0] - vrep = obj.texrep[1] - for vi in range(len(f)): - f[vi][1][0] *= urep - f[vi][1][1] *= vrep - f[vi][1][0] += uoff - f[vi][1][1] += voff - - for vi in range(len(f)): - bface.v.append(mesh.verts[f[vi][0]]) - bface.uv.append((f[vi][1][0], f[vi][1][1])) - mesh.faces.append(bface) - - mesh.mode = 0 - object = Blender.NMesh.PutRaw(mesh) - object.setName(obj.name) - object.setEuler([1.5707963,0,0]) # align ac3d w/ Blender - if self.dads: - dadobj = Blender.Object.get(self.dads.pop()) - dadobj.makeParent([object]) - - print '...done!' + def __init__(self, filename): + + global errmsg + + self.i = 0 + errmsg = '' + self.importdir = Blender.sys.dirname(filename) + try: + file = open(filename, 'r') + except IOError, (errno, strerror): + errmsg = "IOError #%s: %s" % (errno, strerror) + Blender.Draw.PupMenu('ERROR: %s' % errmsg) + inform(errmsg) + return None + header = file.read(5) + header, version = header[:4], header[-1] + if header != 'AC3D': + file.close() + errmsg = 'AC3D header not found (invalid file)' + Blender.Draw.PupMenu('ERROR: %s' % errmsg) + inform(errmsg) + return None + elif version != 'b': + inform('AC3D file version 0x%s.' % version) + inform('This importer is for version 0xb, so it may fail.') + + self.token = {'OBJECT': self.parse_obj, + 'numvert': self.parse_vert, + 'numsurf': self.parse_surf, + 'name': self.parse_name, + 'data': self.parse_data, + 'kids': self.parse_kids, + 'loc': self.parse_loc, + 'rot': self.parse_rot, + 'MATERIAL': self.parse_mat, + 'texture': self.parse_tex, + 'texrep': self.parse_texrep, + 'texoff': self.parse_texoff, + 'crease': self.parse_crease} + + self.objlist = [] + self.mlist = [] + self.dads = [] + self.kids = [] + self.dad = None + + self.lines = file.readlines() + self.lines.append('') + self.parse_file() + file.close() + + self.testAC3DImport() + + def parse_obj(self, value): + if self.kids: + while not self.kids[-1]: + self.kids.pop() + self.dad = self.dad.dad + self.kids[-1] -= 1 + new = Obj(value) + new.dad = self.dad + new.name = value + self.objlist.append(new) + + def parse_kids(self, value): + kids = int(value) + if kids: + self.kids.append(kids) + self.dad = self.objlist[-1] + self.objlist[-1].kids = kids + + def parse_name(self, value): + name = value.split('"')[1] + self.objlist[-1].name = name + + def parse_data(self, value): + data = self.lines[self.i].strip() + self.objlist[-1].data = data + + def parse_tex(self, value): + texture = value.split('"')[1] + self.objlist[-1].tex = texture + + def parse_texrep(self, trash): + trep = self.lines[self.i - 1] + trep = trep.split() + trep = [float(trep[1]), float(trep[2])] + self.objlist[-1].texrep = trep + self.objlist[-1].texoff = [0, 0] + + def parse_texoff(self, trash): + toff = self.lines[self.i - 1] + toff = toff.split() + toff = [float(toff[1]), float(toff[2])] + self.objlist[-1].texoff = toff + + def parse_mat(self, value): + i = self.i - 1 + lines = self.lines + line = lines[i].split() + mat_name = '' + mat_col = mat_amb = mat_emit = mat_spec_col = [0,0,0] + mat_alpha = 1 + mat_spec = 1.0 + + while line[0] == 'MATERIAL': + mat_name = line[1].split('"')[1] + mat_col = map(float,[line[3],line[4],line[5]]) + v = map(float,[line[7],line[8],line[9]]) + mat_amb = (v[0]+v[1]+v[2]) / 3.0 + v = map(float,[line[11],line[12],line[13]]) + mat_emit = (v[0]+v[1]+v[2]) / 3.0 + mat_spec_col = map(float,[line[15],line[16],line[17]]) + mat_spec = float(line[19]) / 64.0 + mat_alpha = float(line[-1]) + mat_alpha = 1 - mat_alpha + self.mlist.append([mat_name, mat_col, mat_amb, mat_emit, mat_spec_col, mat_spec, mat_alpha]) + i += 1 + line = lines[i].split() + + self.i = i + + def parse_rot(self, trash): + i = self.i - 1 + rot = self.lines[i].split(' ', 1)[1] + rot = map(float, rot.split()) + self.objlist[-1].rot = rot + + def parse_loc(self, trash): + i = self.i - 1 + loc = self.lines[i].split(' ', 1)[1] + loc = map(float, loc.split()) + self.objlist[-1].loc = loc + + def parse_crease(self, value): + # AC3D: range is [0.0, 180.0]; Blender: [1, 80] + try: + value = int(value) + except ValueError: + value = int(float(value)) # duh + self.objlist[-1].crease = value + + def parse_vert(self, value): + i = self.i + lines = self.lines + obj = self.objlist[-1] + vlist = obj.vlist + n = int(value) + + while n: + line = lines[i].split() + line = map(float, line) + vlist.append(line) + n -= 1 + i += 1 + + self.i = i + + rot = obj.rot + if rot: + nv = len(vlist) + for j in range(nv): + v = vlist[j] + t = [0,0,0] + t[0] = rot[0]*v[0] + rot[3]*v[1] + rot[6]*v[2] + t[1] = rot[1]*v[0] + rot[4]*v[1] + rot[7]*v[2] + t[2] = rot[2]*v[0] + rot[5]*v[1] + rot[8]*v[2] + vlist[j] = t + + loc = obj.loc + dad = obj.dad + while dad: + for j in [0, 1, 2]: + loc[j] += dad.loc[j] + dad = dad.dad + + for v in vlist: + for j in [0, 1, 2]: + v[j] += loc[j] + + def parse_surf(self, value): + i = self.i + is_smooth = 0 + double_sided = 0 + lines = self.lines + obj = self.objlist[-1] + matlist = obj.matlist + numsurf = int(value) + + while numsurf: + flags = lines[i].split() + flaglow = 0 + if len(flags[1]) > 3: flaglow = int(flags[1][3]) + flaghigh = int(flags[1][2]) + is_smooth = flaghigh & 1 + twoside = flaghigh & 2 + mat = lines[i+1].split() + mat = int(mat[1]) + if not mat in matlist: matlist.append(mat) + refs = lines[i+2].split() + refs = int(refs[1]) + i += 3 + face = [] + faces = [] + fuv = [] + rfs = refs + + while rfs: + line = lines[i].split() + v = int(line[0]) + uv = [float(line[1]), float(line[2])] + face.append([v, uv]) + rfs -= 1 + i += 1 + + if flaglow: + while len(face) >= 2: + cut = face[:2] + faces.append(cut) + face = face[1:] + + if flaglow == 1: + face = [faces[-1][-1], faces[0][0]] + faces.append(face) + + else: + while len(face) > 4: + cut = face[:4] + face = face[3:] + face.insert(0, cut[0]) + faces.append(cut) + + faces.append(face) + + for f in faces: + f.append(mat) + f.append(is_smooth) + f.append(twoside) + self.objlist[-1].flist.append(f) + + numsurf -= 1 + + + self.i = i + + def parse_file(self): + i = 1 + lines = self.lines + line = lines[i].split() + + while line: + kw = '' + for k in self.token.keys(): + if line[0] == k: + kw = k + break + i += 1 + if kw: + self.i = i + self.token[kw](line[1]) + i = self.i + line = lines[i].split() + + def testAC3DImport(self): + global GROUP + scene = Blender.Scene.GetCurrent() + + bmat = [] + for mat in self.mlist: + name = mat[0] + m = Blender.Material.New(name) + m.rgbCol = (mat[1][0], mat[1][1], mat[1][2]) + m.amb = mat[2] + m.emit = mat[3] + m.specCol = (mat[4][0], mat[4][1], mat[4][2]) + m.spec = mat[5] + m.alpha = mat[6] + bmat.append(m) + + for obj in self.objlist: + if obj.type == 'world': + continue + elif obj.type == 'group': + if not GROUP: continue + empty = Blender.Object.New('Empty') + empty.name = obj.name + scene.link(empty) + if self.dads: + dadobj = Blender.Object.get(self.dads.pop()) + dadobj.makeParent([empty]) + while obj.kids: + self.dads.append(empty.name) + obj.kids -= 1 + continue + mesh = Blender.NMesh.New() + if obj.data: mesh.name = obj.data + mesh.setMaxSmoothAngle(obj.crease) # will clamp to [1, 80] + mesh.hasFaceUV(1) + + tex = None + if obj.tex != '': + try: + tex = Blender.Image.Load(obj.tex) + # Commented because it's unnecessary: + #tex.xrep = int(obj.texrep[0]) + #tex.yrep = int(obj.texrep[1]) + except: + basetexname = Blender.sys.basename(obj.tex) + try: + obj.tex = self.importdir + '/' + basetexname + tex = Blender.Image.Load(obj.tex) + except: + try: + obj.tex = TEXTURES_DIR + basetexname + tex = Blender.Image.Load(obj.tex) + except: + inform("Couldn't load texture: %s" % basetexname) + + for v in obj.vlist: + bvert = Blender.NMesh.Vert(v[0],v[1],v[2]) + mesh.verts.append(bvert) + + objmat_indices = [] + for mat in bmat: + if bmat.index(mat) in obj.matlist: + objmat_indices.append(bmat.index(mat)) + mesh.materials.append(mat) + for f in obj.flist: + twoside = f[-1] + is_smooth = f[-2] + fmat = f[-3] + f=f[:-3] + bface = Blender.NMesh.Face() + bface.smooth = is_smooth + if twoside: bface.mode |= Blender.NMesh.FaceModes['TWOSIDE'] + if tex: + bface.mode |= Blender.NMesh.FaceModes['TEX'] + bface.image = tex + bface.materialIndex = objmat_indices.index(fmat) + if obj.texoff: + uoff = obj.texoff[0] + voff = obj.texoff[1] + urep = obj.texrep[0] + vrep = obj.texrep[1] + for vi in range(len(f)): + f[vi][1][0] *= urep + f[vi][1][1] *= vrep + f[vi][1][0] += uoff + f[vi][1][1] += voff + + for vi in range(len(f)): + bface.v.append(mesh.verts[f[vi][0]]) + bface.uv.append((f[vi][1][0], f[vi][1][1])) + mesh.faces.append(bface) + + mesh.mode = 0 + object = Blender.NMesh.PutRaw(mesh) + object.setName(obj.name) + object.setEuler([1.5707963,0,0]) # align ac3d w/ Blender + if self.dads: + dadobj = Blender.Object.get(self.dads.pop()) + dadobj.makeParent([object]) # End of class AC3DImport def filesel_callback(filename): - test = AC3DImport(filename) + + inform("Trying to import AC3D model(s) from %s ..." % filename) + Blender.Window.WaitCursor(1) + starttime = Blender.sys.time() + test = AC3DImport(filename) + Blender.Window.WaitCursor(0) + endtime = Blender.sys.time() - starttime + inform('... done! Data imported in %.3f seconds.\n' % endtime) Blender.Window.FileSelector(filesel_callback, "Import AC3D", "*.ac") diff --git a/release/scripts/bevel_center.py b/release/scripts/bevel_center.py index ededf397ef7..6025cef7f0e 100644 --- a/release/scripts/bevel_center.py +++ b/release/scripts/bevel_center.py @@ -2,7 +2,7 @@ """ Registration info for Blender menus Name: 'Bevel Center' -Blender: 234 +Blender: 236 Group: 'Mesh' Tip: 'Bevel selected vertices' """ @@ -375,6 +375,7 @@ def clear_old(): # Interface # global dist +NV = {} dist = Create(0.2) left = Create(0.0) right = Create(1.0) @@ -392,15 +393,15 @@ def draw(): global EVENT_NOEVENT, EVENT_BEVEL, EVENT_UPDATE, EVENT_RECURS, EVENT_EXIT glClear(GL_COLOR_BUFFER_BIT) - Button("Bevel",EVENT_BEVEL,10,100,280,25) - left=Number('', EVENT_NOEVENT,10,70,45, 20,left.val,0,right.val,'Set the minimum of the slider') - right = Number("",EVENT_NOEVENT,245,70,45,20,right.val,left.val,200,"Set the maximum of the slider") - dist=Slider("Thickness ",EVENT_UPDATE,60,70,180,20,dist.val,left.val,right.val,0,"Thickness of the bevel, can be changed even after bevelling") - glRasterPos2d(8,40) + Button("Bevel",EVENT_BEVEL,10,100,300,25) + left=Number('', EVENT_NOEVENT,10,70,50, 20,left.val,0,right.val,'Set the minimum of the slider') + right = Number("",EVENT_NOEVENT,260,70,50,20,right.val,left.val,200,"Set the maximum of the slider") + dist=Slider("Thickness ",EVENT_UPDATE,65,70,190,20,dist.val,left.val,right.val,0,"Thickness of the bevel, can be changed even after bevelling") + glRasterPos2d(10,40) Text('To finish, you can use recursive bevel to smooth it') - num=Number('', EVENT_NOEVENT,10,10,40, 16,num.val,1,100,'Recursion level') - Button("Recursive",EVENT_RECURS,55,10,100,16) - Button("Exit",EVENT_EXIT,210,10,80,20) + num=Number('', EVENT_NOEVENT,10,10,50, 16,num.val,1,100,'Recursion level') + Button("Recursive",EVENT_RECURS,65,10,100,16) + Button("Exit",EVENT_EXIT,230,10,80,20) def event(evt, val): if ((evt == QKEY or evt == ESCKEY) and not val): @@ -432,7 +433,11 @@ def bevel(): is_editmode = Window.EditMode() if is_editmode: Window.EditMode(0) objects = Blender.Object.GetSelected() - me = NMesh.GetRaw(objects[0].data.name) + bev_obj = objects[0] + if bev_obj.getType() != "Mesh": + PupMenu("ERROR: active object must be a mesh") + return + me = NMesh.GetRaw(bev_obj.getData(name_only = True)) # NF = [] NV = {} @@ -453,7 +458,8 @@ def bevel(): def bevel_update(): """ Use NV to update the bevel """ - global dist, old_dist + global dist, old_dist, NV + if not NV: return is_editmode = Window.EditMode() if is_editmode: Window.EditMode(0) fac = dist.val - old_dist diff --git a/release/scripts/bpymodules/BPyBlender.py b/release/scripts/bpymodules/BPyBlender.py index dda491e3af9..70a2324d0ff 100644 --- a/release/scripts/bpymodules/BPyBlender.py +++ b/release/scripts/bpymodules/BPyBlender.py @@ -33,5 +33,5 @@ basic_modules = [ 'chunk','colorsys','copy','copy_reg','gzip','os','random','repr','stat', 'string','StringIO','types','UserDict','webbrowser','whrandom', 'zlib', 'math', -'BPyBlender' +'BPyBlender', 'BPyRegistry' ] diff --git a/release/scripts/bpymodules/BPyRegistry.py b/release/scripts/bpymodules/BPyRegistry.py new file mode 100644 index 00000000000..33e438be79e --- /dev/null +++ b/release/scripts/bpymodules/BPyRegistry.py @@ -0,0 +1,248 @@ +# -------------------------------------------------------------------------- +# Module BPyRegistry version 0.1 +# Helper functions to store / restore configuration data. +# -------------------------------------------------------------------------- +# $Id$ +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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, +# -------------------------------------------------------------------------- + +# The Registry is a Python dictionary that is kept in Blender for as long as +# the program is running, where scripts can store / restore persistent data +# (data that is not lost when the script exits). This module provides +# functions to save and restore Registry entries as config data in the +# bpydata/config folder. Scripts just need to give an extra parameter to +# the Blender.Registry.Get/Set() functions to have their data automatically +# saved and restored when needed. +# +# Note: entries starting with an underscore are not saved, so script authors +# can use that fact to define data that is not meant to be stored in a +# config file. Example: data to be passed to another script and references to +# invalid data, like Blender objects and any function or method. +# +# Check the Blender.Registry documentation for more information. + +import Blender +from Blender import Registry, sys as bsys + +_EXT = '.cfg' # file extension for saved config data + +# limits: +MAX_ITEMS_NUM = 50 # max number of keys per dict and itens per list and tuple +MAX_STR_LEN = 300 # max string length (remember this is just for config data) + +_CFG_DIR = '' +if Blender.Get('udatadir'): + _CFG_DIR = Blender.sys.join(Blender.Get('udatadir'), 'config') +if not _CFG_DIR or not bsys.exists(_CFG_DIR): + _CFG_DIR = Blender.sys.join(Blender.Get('datadir'), 'config') +if not bsys.exists(_CFG_DIR): + _CFG_DIR = '' + +# to compare against, so we don't write to a cvs tree: +_CVS_SUBPATH = 'release/scripts/bpydata/config/' +if bsys.dirsep == '\\': + _CVS_SUBPATH = _CVS_SUBPATH.replace('/', '\\') + +_KEYS = [k for k in Registry.Keys() if k[0] != '_'] + +def _sanitize(o): + "Check recursively that all objects are valid, set invalid ones to None" + + global MAX_ITEMS_NUM, MAX_STR_LEN + + valid_types = [int, float, bool, long, type] + valid_checked_types = [str, unicode] + # Only very simple types are considered valid for configuration data, + # functions, methods and Blender objects (use their names instead) aren't. + + t = type(o) + + if t == dict: + keys = o.keys() + if len(keys) > MAX_ITEMS_NUM: + return None + for k in keys: + o[k] = _sanitize(o[k]) + elif t in [list, tuple]: + if len(o) > MAX_ITEMS_NUM: + return None + result = [] + for i in o: result.append(_sanitize(i)) + return result + elif t in valid_types: + return o + elif t in valid_checked_types: + if len(o) > MAX_STR_LEN: + o = o[:MAX_STR_LEN] + return o + else: return None + + return o + + +def _dict_to_str(name, d): + "Return a pretty-print version of the passed dictionary" + + if name: l = ['%s = {' % name] + else: l = ['{'] + keys = d.keys() + for k in keys: + if type(d[k]) == dict: + l.append("'%s': %s" % (k, _dict_to_str(None, d[k]))) + else: + l.append("'%s': %s," % (k, repr(d[k]))) + if name: l.append('}') + else: l.append('},') + return "\n".join(l) + +_HELP_MSG = """ +Please create a valid scripts config dir tree either by +copying release/scripts/ tree to your <blenderhome> dir +or by copying release/scripts/bpydata/ tree to a user +defined scripts dir that you can set in the +User Preferences -> Paths tab -> Python path input box. +""" + +def _check_dir(): + global _CFG_DIR, _CVS_SUBPATH, _HELP_MSG + + if not _CFG_DIR: + errmsg = "scripts config dir not found!\n%s" % _HELP_MSG + raise IOError, errmsg + elif _CFG_DIR.find(_CVS_SUBPATH) > 0: + errmsg = """ +Your scripts config dir:\n%s +seems to reside in your local Blender's cvs tree.\n%s""" % (_CFG_DIR, _HELP_MSG) + raise SystemError, errmsg + else: return + + +# API: + +BPY_KEY_MISSING = 0 +BPY_KEY_IN_REGISTRY = 1 +BPY_KEY_IN_FILE = 2 + +def HasConfigData (key): + """ + Check if the given key exists, either already loaded in the Registry dict or + as a file in the script data config dir. + @type key: string + @param key: a given key name. + @returns: + - 0: key does not exist; + - 1: key exists in the Registry dict only; + - 2: key exists as a file only; + - 3: key exists in the Registry dict and also as a file. + @note: for readability it's better to check against the constant bitmasks + BPY_KEY_MISSING = 0, BPY_KEY_IN_REGISTRY = 1 and BPY_KEY_IN_FILE = 2. + """ + + fname = bsys.join(_CFG_DIR, "%s%s" % (key, _EXT)) + + result = BPY_KEY_MISSING + if key in Registry.Keys(): result |= BPY_KEY_IN_REGISTRY + if bsys.exists(fname): result |= BPY_KEY_IN_FILE + + return result + + +def LoadConfigData (key = None): + """ + Load config data from file(s) to the Registry dictionary. + @type key: string + @param key: a given key name. If None (default), all available keys are + loaded. + @returns: None + """ + + _check_dir() + + import os + + if not key: + files = \ + [bsys.join(_CFG_DIR, f) for f in os.listdir(_CFG_DIR) if f[-4:] == _EXT] + else: + files = [] + fname = bsys.join(_CFG_DIR, "%s%s" % (key, _EXT)) + if bsys.exists(fname): files.append(fname) + + for p in files: + f = file(p, 'r') + lines = f.readlines() + f.close() + mainkey = lines[0].split('=')[0].strip() + pysrc = "\n".join(lines) + exec(pysrc) + exec("Registry.SetKey('%s', %s)" % (str(mainkey), mainkey)) + + +def RemoveConfigData (key = None): + """ + Remove this key's config file from the <(u)datadir>/config/ folder. + @type key: string + @param key: the name of the key to be removed. If None (default) all + available config files are deleted. + """ + + _check_dir() + + if not key: + files = \ + [bsys.join(_CFG_DIR, f) for f in os.listdir(_CFG_DIR) if f[-4:] == _EXT] + else: + files = [] + fname = bsys.join(_CFG_DIR, "%s%s" % (key, _EXT)) + if bsys.exists(fname): files.append(fname) + + import os + + for p in files: + os.remove(p) # remove the file(s) + + +def SaveConfigData (key = None): + """ + Save Registry key(s) as file(s) in the <(u)datadir>/config/ folder. + @type key: string + @param key: the name of the key to be saved. If None (default) all + available keys are saved. + """ + + global _KEYS, _CFG_DIR + + _check_dir() + + if key: keys = [key] + else: keys = _KEYS + + for mainkey in keys: + cfgdict = Registry.GetKey(mainkey).copy() + for k in cfgdict.keys(): + if k[0] == '_': cfgdict.pop(k) + + if not cfgdict: continue + + filename = bsys.join(_CFG_DIR, "%s%s" % (mainkey, _EXT)) + f = file(filename, 'w') + output = _dict_to_str(mainkey, _sanitize(cfgdict)) + f.write(output) + f.close() + diff --git a/release/scripts/config.py b/release/scripts/config.py new file mode 100644 index 00000000000..ea91d5e70d0 --- /dev/null +++ b/release/scripts/config.py @@ -0,0 +1,792 @@ +#!BPY + +""" +Name: 'Scripts Config Editor' +Blender: 236 +Group: 'System' +Tooltip: 'View and edit available scripts configuration data' +""" + +__author__ = "Willian P. Germano" +__version__ = "0.1 2005/04/14" +__email__ = ('scripts', 'Author, wgermano:ig*com*br') +__url__ = ('blender', 'elysiun') + +__bpydoc__ ="""\ +This script can be used to view and edit configuration data stored +by other scripts. + +Technical: this data is saved as dictionary keys with the +Blender.Registry module functions. It is persistent while Blender is +running and, if the script's author chose to, is also saved to a file +in the scripts config data dir. + +Usage: + +- Start Screen: + +To access any available key, select it from (one of) the menu(s). + +Hotkeys:<br> + ESC or Q: [Q]uit<br> + H: [H]elp + +- Keys Config Screen: + +This screen exposes the configuration data for the chosen script key. If the +buttons don't fit completely on the screen, you can scroll up or down with +arrow keys or a mouse wheel. Leave the mouse pointer over any button to get +a tooltip about that option. + +Any change can be reverted -- unless you have already applied it. + +If the key is already stored in a config file, there will be a toggle button +(called 'file') that controls whether the changes will be written back to +the file or not. If you just want to change the configuration for the current +session, simply unset that button. Note, though, that data from files has +precedence over those keys already loaded in Blender, so if you re-run this +config editor, unsaved changes will not be seen. + +Hotkeys:<br> + ESC: back to Start Screen<br> + Q: [Q]uit<br> + U: [U]ndo changes<br> + ENTER: apply changes (can't be reverted, then)<br> + UP, DOWN Arrows and mouse wheel: scroll text up / down + +Notes: + +a) Available keys are determined by which scripts you use. If the key you +expect isn't available (or maybe there are none or too few keys), either the +related script doesn't need or still doesn't support this feature or the key +has not been stored yet, in which case you just need to run that script once +to make its config data available. + +b) There are two places where config data files can be saved: the +bpydata/config/ dir (1) inside the default scripts dir or (2) inside the user +defined Python scripts dir +(User Preferences window -> File Paths tab -> Python path). If available, +(2) is the default and also the recommended option, because then fresh Blender +installations won't delete your config data. To use this option, simply set a +dir for Python scripts at the User Preferences window and make sure this dir +has the subdirs bpydata/ and bpydata/config/ inside it. + +c) The key called "General" in the "Other" menu has general config options. +All scripts where that data is relevant are recommended to access it and set +behaviors accordingly. +""" + +# $Id$ +# +# -------------------------------------------------------------------------- +# config.py version 0.1 2005/04/08 +# -------------------------------------------------------------------------- +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 Draw, BGL, Registry, Window, sys as bsys +from Blender.Window import Theme +from BPyRegistry import LoadConfigData, SaveConfigData, HasConfigData,\ + BPY_KEY_IN_FILE, MAX_STR_LEN, MAX_ITEMS_NUM + +# --- +# The "General" configure options key is managed from this script. +verbose = True +confirm_overwrite = True + +tooltips = { + 'verbose': 'print script messages (info, warnings, errors) to the console', + 'confirm_overwrite': 'scripts should always confirm before overwriting files' +} + +CFG_LIST = ['verbose', 'confirm_overwrite', 'tooltips'] +KEY_NAME = 'General' + +def update_registry(): + rd = {} + for var in CFG_LIST: + exec("rd['%s']=%s" % (var, var)) + Registry.SetKey(KEY_NAME, rd, True) + +rd = Registry.GetKey('General', True) +if rd: + try: + for var in CFG_LIST[:-1]: # no need to update tooltips + exec("%s=rd['%s']" % (var, var)) + except: update_registry() + +else: + update_registry() +# --- + +# script globals: +CFGKEY = '' +LABELS = [] +GD = {} # groups dict (includes "Other" for unmapped keys) +INDEX = 0 # to pass button indices to fs callbacks +FREEKEY_IDX = 0 # index of set of keys not mapped to a script name +KEYMENUS = [] +ALL_SCRIPTS = {} +ALL_GROUPS = [] +START_SCREEN = 0 +CONFIG_SCREEN = 1 +DISK_UPDATE = True # write changed data to its config file + +ACCEPTED_TYPES = [bool, int, float, str, unicode] + +SCREEN = START_SCREEN + +SCROLL_DOWN = 0 + +# events: +BEVT_START = 50 +BEVT_EXIT = 0 + BEVT_START +BEVT_BACK = 1 + BEVT_START +BEVT_DISK = 2 + BEVT_START +BEVT_CANCEL = 3 + BEVT_START +BEVT_APPLY = 4 + BEVT_START +BEVT_HELP = 5 + BEVT_START +BEVT_DEL = 6 + BEVT_START +BEVT_KEYMENU = [] +BUT_KEYMENU = [] +BEVT_BOOL = 100 +BEVT_INT = BEVT_BOOL + MAX_ITEMS_NUM +BEVT_FLOAT = BEVT_BOOL + 2*MAX_ITEMS_NUM +BEVT_STR = BEVT_BOOL + 3*MAX_ITEMS_NUM +BEVT_BROWSEDIR = BEVT_BOOL + 4*MAX_ITEMS_NUM +BEVT_BROWSEFILE = BEVT_BOOL + 5*MAX_ITEMS_NUM +BUT_TYPES = { + bool: 0, + int: 0, + float: 0, + str: 0 +} + +# Function definitions: + +def get_keys(): + LoadConfigData() # loads all data from files in (u)scripts/bpydata/config/ + return [k for k in Registry.Keys() if k[0] != "_"] + + +def show_help(script = 'config.py'): + Blender.ShowHelp(script) + + +def fs_dir_callback(pathname): + global CFGKEY, INDEX + + pathname = bsys.dirname(pathname) + datatypes = CFGKEY.sorteddata + datatypes[str][INDEX][1] = pathname + + +def fs_file_callback(pathname): + global CFGKEY, INDEX + + datatypes = CFGKEY.sorteddata + datatypes[str][INDEX][1] = pathname + + +# parse Bpymenus file to get all script filenames +# (used to show help for a given key) +def fill_scripts_dict(): + global ALL_SCRIPTS, ALL_GROUPS + + group = '' + group_len = 0 + sep = bsys.sep + home = Blender.Get('homedir') + if not home: + errmsg = """ +Can't find Blender's home dir and so can't find the +Bpymenus file automatically stored inside it, which +is needed by this script. Please run the +Help -> System -> System Information script to get +information about how to fix this. +""" + raise SystemError, errmsg + fname = bsys.join(home, 'Bpymenus') + if not bsys.exists(fname): return False + f = file(fname, 'r') + lines = f.readlines() + f.close() + for l in lines: + if l.rfind('{') > 0: + group = l.split()[0] + ALL_GROUPS.append(group) + group_len += 1 + continue + elif l[0] != "'": continue + fields = l.split("'") + if len(fields) > 2: + menuname = fields[1].replace('...','') + fields = fields[2].split() + if len(fields) > 1: + fname = fields[1].split(sep)[-1] + ALL_SCRIPTS[fname] = (menuname, group_len - 1) + return True + + +def map_to_registered_script(name): + global ALL_SCRIPTS + + if not name.endswith('.py'): + name = "%s.py" % name + if ALL_SCRIPTS.has_key(name): + return ALL_SCRIPTS[name] # == (menuname, group index) + return None + + +def reset(): + global LABELS, GD, KEYMENUS, KEYS + + # init_data is recalled when a key is deleted, so: + LABELS = [] + GD = {} + KEYMENUS = [] + KEYS = get_keys() + + +# gather all script info, fill gui menus +def init_data(): + global KEYS, GD, ALL_GROUPS, ALL_SCRIPTS, KEYMENUS, LABELS + global BUT_KEYMENU, BEVT_KEYMENU, FREEKEY_IDX + + for k in ALL_GROUPS: + GD[k] = [] + GD[None] = [] + + for k in KEYS: + res = map_to_registered_script(k) + if res: + GD[ALL_GROUPS[res[1]]].append((k, res[0])) + else: GD[None].append((k, k)) + + for k in GD.keys(): + if not GD[k]: GD.pop(k) + + if GD.has_key(None): + GD['Other'] = GD[None] + GD.pop(None) + FREEKEY_IDX = -1 + + BUT_KEYMENU = range(len(GD)) + + for k in GD.keys(): + kmenu = ['Configuration Keys: %s%%t' % k] + for j in GD[k]: + kmenu.append(j[1]) + kmenu = "|".join(kmenu) + KEYMENUS.append(kmenu) + LABELS.append(k) + + if FREEKEY_IDX < 0: + FREEKEY_IDX = LABELS.index('Other') + + length = len(KEYMENUS) + BEVT_KEYMENU = range(1, length + 1) + BUT_KEYMENU = range(length) + + +# for theme colors: +def float_colors(cols): + return map(lambda x: x / 255.0, cols) + + + +class Config: + + def __init__(self, key, has_group = True): + global DISK_UPDATE + + self.key = key + self.has_group = has_group + self.name = key + self.fromdisk = HasConfigData(key) & BPY_KEY_IN_FILE + if not self.fromdisk: DISK_UPDATE = False + else: DISK_UPDATE = True + + self.origdata = Registry.GetKey(key, True) + data = self.data = self.origdata.copy() + + if not data: + Draw.PupMenu('ERROR: couldn\'t find requested data') + self.data = None + return + + keys = data.keys() + nd = {} + for k in keys: + nd[k.lower()] = k + + if nd.has_key('tooltips'): + ndval = nd['tooltips'] + self.tips = data[ndval] + data.pop(ndval) + else: self.tips = 0 + + if nd.has_key('limits'): + ndval = nd['limits'] + self.limits = data[ndval] + data.pop(ndval) + else: self.limits = 0 + + if self.has_group: + scriptname = key + if not scriptname.endswith('.py'): + scriptname = "%s.py" % scriptname + elif nd.has_key('script'): + ndval = nd['script'] + scriptname = data[ndval] + data.pop(ndval) + if not scriptname.endswith('.py'): + scriptname = "%s.py" % scriptname + else: scriptname = None + + self.scriptname = scriptname + + self.sort() + + + def needs_update(self): # check if user changed data + data = self.data + new = self.sorteddata + + for vartype in new.keys(): + for i in new[vartype]: + if data[i[0]] != i[1]: return 1 + + return 0 # no changes + + + def update(self): # update original key + global DISK_UPDATE + + data = self.data + odata = self.origdata + new = self.sorteddata + for vartype in new.keys(): + for i in new[vartype]: + if data[i[0]] != i[1]: data[i[0]] = i[1] + if odata[i[0]] != i[1]: odata[i[0]] = i[1] + + if DISK_UPDATE: Registry.SetKey(self.key, odata, True) + + def delete(self): + global DISK_UPDATE + + delmsg = 'OK?%t|Delete key from memory' + if DISK_UPDATE: + delmsg = "%s and from disk" % delmsg + if Draw.PupMenu(delmsg) == 1: + Registry.RemoveKey(self.key, DISK_UPDATE) + return True + + return False + + + def revert(self): # revert to original key + data = self.data + new = self.sorteddata + for vartype in new.keys(): + for i in new[vartype]: + if data[i[0]] != i[1]: i[1] = data[i[0]] + + + def sort(self): # create a new dict with types as keys + global ACCEPTED_TYPES, BUT_TYPES + + data = self.data + datatypes = {} + keys = [k for k in data.keys() if k[0] != '_'] + for k in keys: + val = data[k] + tval = type(val) + if tval not in ACCEPTED_TYPES: continue + if not datatypes.has_key(tval): + datatypes[tval] = [] + datatypes[type(val)].append([k, val]) + if datatypes.has_key(unicode): + if not datatypes.has_key(str): datatypes[str] = datatypes[unicode] + else: + for i in datatypes[unicode]: datatypes[str].append(i) + datatypes.pop(unicode) + for k in datatypes.keys(): + dk = datatypes[k] + dk.sort() + dk.reverse() + BUT_TYPES[k] = range(len(dk)) + self.sorteddata = datatypes + + +# GUI: + +# gui callbacks: + +def gui(): # drawing the screen + + global SCREEN, START_SCREEN, CONFIG_SCREEN, KEYMENUS, LABELS + global BEVT_KEYMENU, BUT_KEYMENU, CFGKEY + global BUT_TYPES, SCROLL_DOWN, VARS_NUM + + WIDTH, HEIGHT = Window.GetAreaSize() + + theme = Theme.Get()[0] + tui = theme.get('ui') + ttxt = theme.get('text') + + COL_BG = float_colors(ttxt.back) + COL_TXT = ttxt.text + COL_TXTHI = ttxt.text_hi + + BGL.glClearColor(COL_BG[0],COL_BG[1],COL_BG[2],COL_BG[3]) + BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) + BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2]) + + if SCREEN == START_SCREEN: + x = 10 + y = 10 + h = 20 + w = 90 + BGL.glRasterPos2i(x, y) + Draw.Text('Select a configuration key to access it. Press Q or ESC to leave.') + km_len = len(KEYMENUS) + km_columns = (WIDTH - x) / w + if km_columns == 0: km_rows = km_len + else: + km_rows = km_len / km_columns + if (km_len % km_columns): km_rows += 1 + if km_rows == 0: km_rows = 1 + ystart = y + 2*h*km_rows + if ystart > (HEIGHT - 70): ystart = HEIGHT - 70 + y = ystart + column = 1 + for i, km in enumerate(KEYMENUS): + column += 1 + BGL.glRasterPos2i(x + 2, y + h + 5) + Draw.Text(LABELS[i]) + BUT_KEYMENU[i] = Draw.Menu(km, BEVT_KEYMENU[i], + x, y, w - 10, h, 0, 'Choose a key to access its configuration data') + if column > km_columns: + column = 1 + y -= 2*h + if y < 35: break + x = 10 + else: x += w + x = 10 + y = 50 + ystart + BGL.glColor3ub(COL_TXTHI[0], COL_TXTHI[1], COL_TXTHI[2]) + BGL.glRasterPos2i(x, y) + Draw.Text('Scripts Configuration Editor') + Draw.PushButton('help', BEVT_HELP, x, 22, 45, 16, + 'View help information about this script (hotkey: H)') + + elif SCREEN == CONFIG_SCREEN: + x = y = 10 + h = 18 + data = CFGKEY.sorteddata + tips = CFGKEY.tips + fromdisk = CFGKEY.fromdisk + limits = CFGKEY.limits + VARS_NUM = 0 + for k in data.keys(): + VARS_NUM += len(data[k]) + lines = VARS_NUM + 5 # to account for header and footer + y = lines*h + if y > HEIGHT - 20: y = HEIGHT - 20 + BGL.glColor3ub(COL_TXTHI[0],COL_TXTHI[1], COL_TXTHI[2]) + BGL.glRasterPos2i(x, y) + Draw.Text('Scripts Configuration Editor') + y -= 20 + BGL.glColor3ub(COL_TXT[0],COL_TXT[1], COL_TXT[2]) + txtsize = 10 + if HEIGHT < lines*h: + BGL.glRasterPos2i(10, 5) + txtsize += Draw.Text('Arrow keys or mouse wheel to scroll, ') + BGL.glRasterPos2i(txtsize, 5) + Draw.Text('Q or ESC to return.') + BGL.glRasterPos2i(x, y) + Draw.Text('Key: "%s"' % CFGKEY.name) + bh = 16 + bw = 45 + by = 16 + i = -1 + if CFGKEY.scriptname: + i = 0 + Draw.PushButton('help', BEVT_HELP, x, by, bw, bh, + 'Show documentation for the script that owns this key (hotkey: H)') + Draw.PushButton('back', BEVT_BACK, x + (1+i)*bw, by, bw, bh, + 'Back to config keys selection screen (hotkey: ESC)') + Draw.PushButton('exit', BEVT_EXIT, x + (2+i)*bw, by, bw, bh, + 'Exit from Scripts Config Editor (hotkey: Q)') + Draw.PushButton('revert', BEVT_CANCEL, x + (3+i)*bw, by, bw, bh, + 'Revert data to original values (hotkey: U)') + Draw.PushButton('apply', BEVT_APPLY, x + (4+i)*bw, by, bw, bh, + 'Apply changes, if any (hotkey: ENTER)') + delmsg = 'Delete this data key from memory' + if fromdisk: delmsg = "%s and from disk" % delmsg + Draw.PushButton('delete', BEVT_DEL, x + (5+i)*bw, by, bw, bh, + '%s (hotkey: DELETE)' % delmsg) + if fromdisk: + Draw.Toggle("file", BEVT_DISK, x + 3 + (6+i)*bw, by, bw, bh, DISK_UPDATE, + 'Update also the file where this config key is stored') + i = -1 + top = -1 + y -= 20 + yend = 30 + if data.has_key(bool) and y > 0: + lst = data[bool] + for l in lst: + top += 1 + i += 1 + if top < SCROLL_DOWN: continue + y -= h + if y < yend: break + w = 20 + tog = data[bool][i][1] + if tips and tips.has_key(l[0]): tooltip = tips[l[0]] + else: tooltip = "click to toggle" + BUT_TYPES[bool][i] = Draw.Toggle("", BEVT_BOOL + i, + x, y, w, h, tog, tooltip) + BGL.glRasterPos2i(x + w + 3, y + 5) + Draw.Text(l[0].lower().replace('_', ' ')) + i = -1 + y -= 5 + if data.has_key(int) and y > 0: + lst = data[int] + for l in lst: + w = 70 + top += 1 + i += 1 + if top < SCROLL_DOWN: continue + y -= h + if y < yend: break + val = data[int][i][1] + if limits: min, max = limits[l[0]] + else: min, max = 0, 10 + if tips and tips.has_key(l[0]): tooltip = tips[l[0]] + else: tooltip = "click / drag to change" + BUT_TYPES[int][i] = Draw.Number("", BEVT_INT + i, + x, y, w, h, val, min, max, tooltip) + BGL.glRasterPos2i(x + w + 3, y + 3) + Draw.Text(l[0].lower().replace('_', ' ')) + i = -1 + y -= 5 + if data.has_key(float) and y > 0: + lst = data[float] + for l in lst: + w = 70 + top += 1 + i += 1 + if top < SCROLL_DOWN: continue + y -= h + if y < yend: break + val = data[float][i][1] + if limits: min, max = limits[l[0]] + else: min, max = 0.0, 1.0 + if tips and tips.has_key(l[0]): tooltip = tips[l[0]] + else: tooltip = "click and drag to change" + BUT_TYPES[float][i] = Draw.Number("", BEVT_FLOAT + i, + x, y, w, h, val, min, max, tooltip) + BGL.glRasterPos2i(x + w + 3, y + 3) + Draw.Text(l[0].lower().replace('_', ' ')) + i = -1 + y -= 5 + if data.has_key(str) and y > 0: + lst = data[str] + for l in lst: + top += 1 + i += 1 + if top < SCROLL_DOWN: continue + y -= h + if y < yend: break + name = l[0].lower() + is_dir = is_file = False + if name.find('_dir', -4) > 0: is_dir = True + elif name.find('_file', -5) > 0: is_file = True + w = WIDTH - 20 + wbrowse = 50 + if is_dir and w > wbrowse: w -= wbrowse + if tips and tips.has_key(l[0]): tooltip = tips[l[0]] + else: tooltip = "click to write a new string" + name = name.replace('_',' ') + ': ' + BUT_TYPES[str][i] = Draw.String(name, BEVT_STR + i, + x, y, w, h, l[1], MAX_STR_LEN, tooltip) + if is_dir: + Draw.PushButton('browse', BEVT_BROWSEDIR + i, x+w+1, y, wbrowse, h, + 'click to open a file selector (pick any file in the desired dir)') + elif is_file: + Draw.PushButton('browse', BEVT_BROWSEFILE + i, x + w + 1, y, 50, h, + 'click to open a file selector') + + +def fit_scroll(): + global SCROLL_DOWN, VARS_NUM + max = VARS_NUM - 1 # so last item is always visible + if SCROLL_DOWN > max: + SCROLL_DOWN = max + elif SCROLL_DOWN < 0: + SCROLL_DOWN = 0 + + +def event(evt, val): # input events + + global SCREEN, START_SCREEN, CONFIG_SCREEN + global SCROLL_DOWN, CFGKEY + + if not val: return + + if evt == Draw.ESCKEY: + if SCREEN == START_SCREEN: Draw.Exit() + else: + if CFGKEY.needs_update(): + if Draw.PupMenu('UPDATE?%t|Data was changed') == 1: + CFGKEY.update() + SCREEN = START_SCREEN + SCROLL_DOWN = 0 + Draw.Redraw() + return + elif evt == Draw.QKEY: + if SCREEN == CONFIG_SCREEN and CFGKEY.needs_update(): + if Draw.PupMenu('UPDATE?%t|Data was changed') == 1: + CFGKEY.update() + Draw.Exit() + return + elif evt == Draw.HKEY: + if SCREEN == START_SCREEN: show_help() + elif CFGKEY.scriptname: show_help(CFGKEY.scriptname) + return + + elif SCREEN == CONFIG_SCREEN: + if evt in [Draw.DOWNARROWKEY, Draw.WHEELDOWNMOUSE]: + SCROLL_DOWN += 1 + fit_scroll() + elif evt in [Draw.UPARROWKEY, Draw.WHEELUPMOUSE]: + SCROLL_DOWN -= 1 + fit_scroll() + elif evt == Draw.UKEY: + if CFGKEY.needs_update(): + CFGKEY.revert() + elif evt == Draw.RETKEY or evt == Draw.PADENTER: + if CFGKEY.needs_update(): + CFGKEY.update() + elif evt == Draw.DELKEY: + if CFGKEY.delete(): + reset() + init_data() + SCREEN = START_SCREEN + SCROLL_DOWN = 0 + else: return + Draw.Redraw() + + +def button_event(evt): # gui button events + + global SCREEN, START_SCREEN, CONFIG_SCREEN, CFGKEY, DISK_UPDATE + global BEVT_KEYMENU, BUT_KEYMENU, BUT_TYPES, SCROLL_DOWN, GD, INDEX + global BEVT_EXIT, BEVT_BACK, BEVT_APPLY, BEVT_CANCEL, BEVT_HELP, FREEKEY_IDX + + if SCREEN == START_SCREEN: + for e in BEVT_KEYMENU: + if evt == e: + index = e - 1 + k = BUT_KEYMENU[index].val - 1 + CFGKEY = Config(GD[LABELS[index]][k][0], index != FREEKEY_IDX) + if CFGKEY.data: + SCREEN = CONFIG_SCREEN + Draw.Redraw() + return + if evt == BEVT_EXIT: + Draw.Exit() + elif evt == BEVT_HELP: + show_help() + return + + elif SCREEN == CONFIG_SCREEN: + datatypes = CFGKEY.sorteddata + if evt >= BEVT_BROWSEFILE: + INDEX = evt - BEVT_BROWSEFILE + Window.FileSelector(fs_file_callback, 'Choose file') + elif evt >= BEVT_BROWSEDIR: + INDEX = evt - BEVT_BROWSEDIR + Window.FileSelector(fs_dir_callback, 'Choose any file') + elif evt >= BEVT_STR: + var = BUT_TYPES[str][evt - BEVT_STR].val + datatypes[str][evt - BEVT_STR][1] = var + elif evt >= BEVT_FLOAT: + var = BUT_TYPES[float][evt - BEVT_FLOAT].val + datatypes[float][evt - BEVT_FLOAT][1] = var + elif evt >= BEVT_INT: + var = BUT_TYPES[int][evt - BEVT_INT].val + datatypes[int][evt - BEVT_INT][1] = var + elif evt >= BEVT_BOOL: + var = datatypes[bool][evt - BEVT_BOOL][1] + if var == True: var = False + else: var = True + datatypes[bool][evt - BEVT_BOOL][1] = var + + elif evt == BEVT_BACK: + if SCREEN == CONFIG_SCREEN: + SCREEN = START_SCREEN + SCROLL_DOWN = 0 + Draw.Redraw() + elif evt == BEVT_EXIT: + if CFGKEY.needs_update(): + if Draw.PupMenu('UPDATE?%t|Data was changed') == 1: + CFGKEY.update() + Draw.Exit() + return + elif evt == BEVT_APPLY: + if CFGKEY.needs_update(): + CFGKEY.update() + elif evt == BEVT_CANCEL: + if CFGKEY.needs_update(): + CFGKEY.revert() + elif evt == BEVT_DEL: + if CFGKEY.delete(): + reset() + init_data() + SCREEN = START_SCREEN + SCROLL_DOWN = 0 + elif evt == BEVT_DISK: + if DISK_UPDATE: DISK_UPDATE = False + else: DISK_UPDATE = True + elif evt == BEVT_HELP: + show_help(CFGKEY.scriptname) + return + else: + return + Draw.Redraw() + +# End of definitions + + +KEYS = get_keys() + +if not KEYS: + Draw.PupMenu("NO DATA: please read this help screen") + Blender.ShowHelp('config.py') +else: + fill_scripts_dict() + init_data() + Draw.Register(gui, event, button_event) diff --git a/release/scripts/disp_paint.py b/release/scripts/disp_paint.py index 01a6da8cc98..e7628a00cae 100644 --- a/release/scripts/disp_paint.py +++ b/release/scripts/disp_paint.py @@ -1,7 +1,7 @@ #!BPY """ Registration info for Blender menus: <- these words are ignored -Name: 'dispaint' +Name: 'Dispaint' Blender: 233 Group: 'Mesh' Tip: 'use vertex paint color value to modify shape displacing vertices along normal' @@ -126,6 +126,9 @@ E_NOISEBAS = 54 E_NOISEVAL=[E_NOISEH,E_NOISELAC,E_NOISEOCT,E_NOISEOFF,E_NOISEBAS] E_NOISEDIM = 55 +ExitTIP="Exit from this script session " +CreateTIP="Create a new copy of the selected shape" +ActionTIP="Do the current selected actions" def copy_transform(ozero,Obis): @@ -241,7 +244,8 @@ def paint(): traite_face(f) else: traite_face(f) - Me[0].link(me) + #Me[0].link(me) + me.update() Me[0].makeDisplayList() except: ERROR=2 @@ -354,7 +358,7 @@ def draw(): global mat, ORIName, NEWName, ORIENTMenu global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu, NOISEDIMbout,NOISEDIM global HBout,lacunarityBout,octavesBout,offsetBout,basisBout - global noiseTYPE + global noiseTYPE, ExitTIP, CreateTIP, ActionTIP size=Buffer(GL_FLOAT, 4) glGetFloatv(GL_SCISSOR_BOX, size) @@ -378,9 +382,9 @@ def draw(): n0=70 n1=55 - Button("Create" ,E_CREATE ,5 ,size[3]-n0+16 ,60 ,20) - Button("Action" ,E_ACTION ,5 ,size[3]-n0-4 ,60 ,20) - Button("Exit" ,E_EXIT ,5 ,size[3]-n0-24 ,60 ,20) + Button("Create" ,E_CREATE ,5 ,size[3]-n0+16 ,60 ,20,CreateTIP) + Button("Action" ,E_ACTION ,5 ,size[3]-n0-4 ,60 ,20,ActionTIP) + Button("Exit" ,E_EXIT ,5 ,size[3]-n0-24 ,60 ,20,ExitTIP) NRepeat=Number("repeat" ,E_REPEAT ,5 ,size[3]-n0-50 ,75 ,20, NRepeat.val,1,10) diff --git a/release/scripts/doc_browser.py b/release/scripts/doc_browser.py index 9c8d77caca6..2a6273f15c7 100644 --- a/release/scripts/doc_browser.py +++ b/release/scripts/doc_browser.py @@ -3,7 +3,7 @@ """ Name: 'BPy Doc Browser' Blender: 232 -Group: 'Misc' +Group: 'System' Tip: 'Browse BPython (scripting API) modules doc strings.' """ @@ -15,8 +15,11 @@ The "Doc Browser" lets users navigate the documentation strings of part of the Blender Python API. It doesn't give access yet to object method functions and variables, only to -module functions, but still it is a handy reference. Specially for quick -access, for example to Blender.BGL: the module that wraps OpenGL calls. +module functions, but still it is a handy reference. + +Hotkeys:<br> + Page Up / Page Down: scroll 5 lines at a time;<br> + Up / Down arrow keys or mouse wheel: scroll one line at a time. Notes:<br> Everyone interested in the bpython api is also invited to read "The Blender @@ -199,7 +202,7 @@ BACK_MODULE= 7 CLOSE_VIEW= 8 FILTER_DISPLAY= 9 -SCROLLBAR= 10 +#SCROLLBAR= 10 VIEW_DOC= 100 BROWSE_MODULE= 20000 @@ -325,8 +328,8 @@ def draw(): if (items>len(browselist)): items= len(browselist) end= len(browselist)-items - if (end>0): - scr= Scrollbar(SCROLLBAR, table[2]+5, table[1], 20, table[3]-table[1], scr.val, 0.0, end, 0, "Page Up/Down scrolls list.") + #if (end>0): + # scr= Scrollbar(SCROLLBAR, table[2]+5, table[1], 20, table[3]-table[1], scr.val, 0.0, end, 0, "Page Up/Down scrolls list.") row= table row[1]= row[3]-row_height @@ -404,17 +407,27 @@ def draw(): if (eindex<doc_lines): Button("Page down", DOC_PAGE_DOWN, table[2]-100, table[1]+5, 90, 18) lmouse= [0, 0] + +def fit_scroll(): + global browse_scrollstart, browselist + if (browse_scrollstart<0): browse_scrollstart= 0 + elif (browse_scrollstart>=len(browselist)): browse_scrollstart= len(browselist)-1 + def event(evt, val): global browse_scrollstart if (evt==QKEY or evt==ESCKEY): Exit() - elif (evt in [PAGEUPKEY, PAGEDOWNKEY] and val): - if (evt==PAGEUPKEY): browse_scrollstart= browse_scrollstart-5 - else: browse_scrollstart= browse_scrollstart+5 - - if (browse_scrollstart<0): browse_scrollstart= 0 - elif (browse_scrollstart>=len(browselist)): browse_scrollstart= len(browselist)-1 - + elif val: + if (evt in [PAGEUPKEY, PAGEDOWNKEY]): + if (evt==PAGEUPKEY): browse_scrollstart= browse_scrollstart-5 + else: browse_scrollstart= browse_scrollstart+5 + elif (evt in [UPARROWKEY, WHEELUPMOUSE]): + browse_scrollstart -= 1 + elif (evt in [DOWNARROWKEY, WHEELDOWNMOUSE]): + browse_scrollstart += 1 + else: return + + fit_scroll() Redraw() def bevent(evt): @@ -431,9 +444,9 @@ def bevent(evt): elif (evt==CLOSE_VIEW): view_doc(-1) elif (evt==FILTER_DISPLAY): toggle_function_filter() - elif (evt==SCROLLBAR): - global browse_scrollstart - browse_scrollstart= int(scr.val) + #elif (evt==SCROLLBAR): + # global browse_scrollstart + # browse_scrollstart= int(scr.val) elif (evt>=BROWSE_MODULE): browse_module(evt-BROWSE_MODULE) elif (evt>=VIEW_DOC): view_doc(evt-VIEW_DOC) diff --git a/release/scripts/fixfromarmature.py b/release/scripts/fixfromarmature.py index c60978831fc..9e11702c0be 100644 --- a/release/scripts/fixfromarmature.py +++ b/release/scripts/fixfromarmature.py @@ -64,8 +64,19 @@ Select the mesh and run this script. A fixed copy of it will be created. # -------------------------------------------------------------------------- import Blender -try: - Ozero=Blender.Object.GetSelected()[0] + +Ozero=Blender.Object.GetSelected()[0] + +errormsg = '' +if not Ozero: + errormsg = "no mesh object selected" +elif Ozero.getType() != "Mesh": + errormsg = "selected (active) object must be a mesh" + +if errormsg: + Blender.Draw.PupMenu("ERROR: %s" % errormsg) + +else: nomdelobjet=Ozero.getName() Mesh=Blender.NMesh.GetRawFromObject(nomdelobjet) Obis = Blender.Object.New ('Mesh') @@ -73,5 +84,17 @@ try: Obis.setMatrix(Ozero.getMatrix()) scene = Blender.Scene.getCurrent() scene.link (Obis) -except: - Blender.Draw.PupMenu("Error%t|Not a mesh or no object selected") + + Mesh2=Obis.getData() + Mesh1=Ozero.getData() + + if len(Mesh2.verts)==len(Mesh1.verts): + for VertGroupName in Mesh1.getVertGroupNames(): + VertexList = Mesh1.getVertsFromGroup(VertGroupName, True) + Mesh2.addVertGroup(VertGroupName) + for Vertex in VertexList: + Mesh2.assignVertsToGroup(VertGroupName, [Vertex[0]], Vertex[1], 'add') + else: + for vgroupname in Ozero.getVertGroupNames(): + Mesh2.addVertGroup(vgroupname) + Mesh2.update() diff --git a/release/scripts/help_browser.py b/release/scripts/help_browser.py index bcac4b791d2..aa05c988207 100644 --- a/release/scripts/help_browser.py +++ b/release/scripts/help_browser.py @@ -179,6 +179,16 @@ PATHS = { 'uscripts': Blender.Get('uscriptsdir') } +if not PATHS['home']: + errmsg = """ +Can't find Blender's home dir and so can't find the +Bpymenus file automatically stored inside it, which +is needed by this script. Please run the +Help -> System -> System Information script to get +information about how to fix this. +""" + raise SystemError, errmsg + BPYMENUS_FILE = bsys.join(PATHS['home'], 'Bpymenus') f = file(BPYMENUS_FILE, 'r') diff --git a/release/scripts/sysinfo.py b/release/scripts/sysinfo.py index daa6867e1a8..01cff8ae57f 100644 --- a/release/scripts/sysinfo.py +++ b/release/scripts/sysinfo.py @@ -12,13 +12,13 @@ __url__ = ("blender", "elysiun") __version__ = "1.1" __bpydoc__ = """\ This script creates a text in Blender's Text Editor with information -about your OS, video card, OpenGL driver, Blender and Python versions and -more. +about your OS, video card, OpenGL driver, Blender and Python versions, +script related paths and more. -If you are experiencing trouble running Blender or its scripts in general, -this information can be useful for online searches (like checking if there -are known issues related to your video card) or to get help from other users -or the program's developers. +If you are experiencing trouble running Blender itself or any Blender Python +script, this information can be useful to fix any problems or at least for +online searches (like checking if there are known issues related to your +video card) or to get help from other users or the program's developers. """ # $Id$ @@ -85,7 +85,8 @@ def textWrap(text, length = 70): # msg = sys.exc_info()[1].__str__().split()[3] # Blender.Draw.PupMenu("Python error:|This script requires the %s module" %msg) -header = "= Blender %s System Information =" % Blender.Get("version") +version = Blender.Get('version') / 100.0 +header = "= Blender %s System Information =" % version lilies = len(header)*"="+"\n" header = lilies + header + "\n" + lilies @@ -97,40 +98,12 @@ output.write("Platform: %s\n========\n\n" % sys.platform) output.write("Python:\n======\n\n") output.write("- Version: %s\n\n" % sys.version) -output.write("- Path:\n\n") +output.write("- Paths:\n\n") for p in sys.path: output.write(p + '\n') output.write("\n- Directories:") -if not Blender.Get('homedir'): - outmsg = """ - -<WARNING> - Blender home dir not found! - This should probably be "<path>/.blender/" - where <path> is usually the user's home dir. - - Blender's home dir is where entries like: - folders scripts/, plugins/ and locale/ and - files .Blanguages and .bfont.ttf - are located. - - It's also where Blender stores the Bpymenus file - with information about registered scripts, so it - only needs to scan scripts dir(s) when they are - modified. -""" - output.write(outmsg) - if Blender.Get('scriptsdir').find('release') > 0: - output.write(""" -It seems this Blender binary is located at its cvs source tree: -that's ok, but the scripts registration data will be recreated -from dir(s) whenever you start the program, instead of only -when those dirs are modified. - -Adding a .blender/ subdir to e. g. your home dir can prevent that. -""") - dirlist = [ ['homedir', 'Blender home dir', 1], ['scriptsdir', 'Default dir for scripts', 1], @@ -139,11 +112,14 @@ dirlist = [ ['udatadir', 'Data dir "bpydata/" inside user defined dir', 0] ] +has_dir = {} + for dir in dirlist: dirname, dirstr, is_critical = dir dirpath = Blender.Get(dirname) output.write("\n\n %s:\n" % dirstr) if not dirpath: + has_dir[dirname] = False if is_critical: warnings += 1 output.write(" <WARNING> -- not found") @@ -152,24 +128,34 @@ for dir in dirlist: output.write(" <NOTICE> -- not found") else: output.write(" %s" % dirpath) + has_dir[dirname] = True -configdir = bsys.join(Blender.Get('datadir'), 'config') -output.write('\n\n Default config data "bpydata/config/" dir:') -if bsys.exists(configdir): - output.write(" %s" % configdir) -else: - warnings += 1 - output.write(""" - <WARNING> -- not found. - config/ should be inside the default scripts *data dir*. - It's used by Blender to store scripts configuration data. -""") +if not has_dir['homedir']: + outmsg = """ + +<WARNING> - Blender home dir not found! + This should probably be "<path>/.blender/" + where <path> is usually the user's home dir. -if Blender.Get('udatadir'): + Blender's home dir is where entries like: + folders scripts/, plugins/ and locale/ and + files .Blanguages and .bfont.ttf + are located. + + It's also where Blender stores the Bpymenus file + with information about registered scripts, so it + only needs to scan scripts dir(s) when they are + modified. +""" + output.write(outmsg) + +has_uconfdir = False +if has_dir['udatadir']: uconfigdir = bsys.join(Blender.Get('udatadir'), 'config') output.write("\n\n User defined config data dir:") - if bsys.exists(configdir): - output.write(" %s" % configdir) + if bsys.exists(uconfigdir): + has_uconfdir = True + output.write(" %s" % uconfigdir) else: notices += 1 output.write(""" @@ -180,6 +166,49 @@ if Blender.Get('udatadir'): won't overwrite the data.) """) +configdir = bsys.join(Blender.Get('datadir'), 'config') +output.write('\n\n Default config data "bpydata/config/" dir:\n') +if bsys.exists(configdir): + output.write(" %s" % configdir) +else: + warnings += 1 + output.write("""<WARNING> -- not found. + config/ should be inside the default scripts *data dir*. + It's used by Blender to store scripts configuration data + when <user defined scripts dir>/bpydata/config/ dir is + not available. +""") + +if has_uconfdir: + output.write(""" + +The user defined config dir will be used. +""") + +cvsdir = 'release/scripts' +if bsys.dirsep == '\\': cvsdir = cvsdir.replace('/','\\') +sdir = Blender.Get('scriptsdir') +if sdir and sdir.find(cvsdir) >= 0: + if has_uconfdir: + notices += 1 + output.write("\n\n<NOTICE>:\n") + else: + warnings += 1 + output.write("\n\n<WARNING>:\n") + output.write(""" +It seems this Blender binary is located in its cvs source tree. + +It's recommended that the release/scripts/ dir tree is copied +to your blender home dir. +""") + if not has_uconfdir: + output.write(""" +Since you also don't have a user defined scripts dir with the +bpydata/config dir inside it, it will not be possible to save +and restore scripts configuration data files, since writing +to a dir inside a cvs tree is not a good idea and is avoided. +""") + missing_mods = [] # missing basic modules try: @@ -197,11 +226,19 @@ Some expected modules were not found. Because of that some scripts bundled with Blender may not work. Please read the FAQ in the Readme.html file shipped with Blender for information about how to fix the problem. -Missing modules:""" +Missing modules: +""" output.write(outmsg) warnings += 1 for m in missing_mods: output.write('-> ' + m + '\n') + if 'BPyRegistry' in missing_mods: + output.write(""" +Module BPyRegistry.py is missing! +Without this module it's not possible to save and restore +scripts configuration data files. +""") + else: output.write("\n\n- Modules: all basic ones were found.\n") diff --git a/release/scripts/unweld.py b/release/scripts/unweld.py new file mode 100644 index 00000000000..7212574cac5 --- /dev/null +++ b/release/scripts/unweld.py @@ -0,0 +1,439 @@ +#!BPY +""" Registration info for Blender menus: <- these words are ignored +Name: 'Unweld' +Blender: 234 +Group: 'Mesh' +Tip: 'Unweld all faces from a (or several) selected and common vertex. Made vertex bevelling' +""" + +__author__ = "Jean-Michel Soler (jms)" +__url__ = ("blender", "elysiun", +"Script's homepage, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_faces2vertex.htm#exemple", +"Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender") +__version__ = "0.4.5 beta" + +__bpydoc__ = """\ +This script unwelds faces from a selected vertex. + +There's also experimental support for static or dynamic (move mouse in the +Scripts window) vertex bevel. + +Usage: + +Select a vertex, then run this script. Its options are: + +- unbind points;<br> +- with noise;<br> +- middle face;<br> +- static bevel vertex;<br> +- moving bevel vertex; +""" + +# ------------------------------------------ +# Un-Weld script 0.4.5 beta +name="UnWeld" +Tip= 'Unweld all faces from a selected and common vertex. Made vertex bevelling' +# +# split all faces from one selected vertex +# (c) 2004 J-M Soler released under Blender Artistic License +#---------------------------------------------- +# Official Page : +website = 'http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_faces2vertex.htm#exemple' +# Communicate problems and errors on: +community = 'http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender' +#---------------------------------------------- +# Blender Artistic License +# http://download.blender.org/documentation/html/x21254.html +#--------------------------------------------- +# Changelog +#---------------------------------------------- +# 25/05 : +# -- separate choise, normal (same place) or spread at random, middle of the face +# -- works on several vertices too +# -- Quite vertex bevelling on <<lone>> vertex : create hole in faces around this +# vertex +# 03/06 : +# -- a sort of "bevelled vertex" extrusion controled by horizontal mouse +# displacement. just a beta test to the mouse control. +# 08/08 : +# -- minor correction to completely disconnect face. +#---------------------------------------------- +# Page officielle : +# http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_faces2vertex.htm#exemple +# Communiquer les problemes et erreurs sur: +# http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender +# Blender Artistic License +# http://download.blender.org/documentation/html/x21254.html +#--------------------------------------------- +# Changelog +#---------------------------------------------- +# 25/05 : +# -- separation du choix, normal ou dispersion hasardeuse, +# milieu de face +# -- sommets multiples / +# -- presque unvertex bevelling sur un vertex solitaire : cree +# un trou dans la facette autour du sommet +# 03/06 : +# -- une sorte de vertex extruder en biseau, controle par +# deplacement horizontal de la souris +# 08/08 : +# -- correction mineure pour s'assurer que les faces soient +# entierment deconnectees +#---------------------------------------------- + +import Blender +from Blender import Noise +from Blender.Draw import * +from Blender.BGL import * + +# $Id$ +Blender.Window.EditMode(0) + + +def autoDocHelp_script(name,Tip,website): + try: + dir = Blender.Get('datadir') + scriptdir=dir[:dir.find(dir.split(Blender.sys.sep)[-2])]+'scripts'+Blender.sys.sep + ttt="""#!BPY\n\"\"\"\nName: '%s'\nBlender: 234 +Group: 'HelpWebsites'\nTooltip: '%s'\n\"\"\" +import Blender, webbrowser\nwebbrowser.open('%s')\n"""%(name,Tip,website) + fil=open(scriptdir+'Help_%s.py'%name.replace(' ','_'),'w') + fil.write(ttt) + fil.close() + ttt="""#!BPY\n\"\"\"\nName: 'A french speaking users community'\nBlender: 234\nGroup: 'HelpWebsites'\nTooltip: 'aA french community News Portal, zoo-Blender.'\n\"\"\" +import Blender,webbrowser +webbrowser.open('http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender')\n""" + fil=open(scriptdir+'Help_frenchusers.py','w') + fil.write(ttt) + fil.close() + except: + pass + +autoDocHelp_script(name,Tip,website) + +Nr=Noise.random +decal=0.03 +t=[0.0,0.0,0.0] +pl=[] +orig=[] + +DEBUG = 0 +SUBSURF=0 +DIM=Create(1.0) + +def Tampon(v,t): + for n in range(len(v)): t[n]=t[n]+v[n] + return t + +def freeTampon(t): + for n in range(3): t[n]=0.0 + return t + +def TamponMoyen(t,f): + for n in range(3): t[n]/=len(f) + return t + +def appliqueMoyenne(v,t): + for n in range(len(v)): v[n]=t[n] + return v + +def docF(f0,f): + try: + f0.mat=f.mat + f0.uv=f.uv + f0.col=f.col + f0.image=f.image + f0.smooth=f.smooth + f0.mode=f.mode + f0.flag=f.flag + return f0 + except: + pass + +def listConnecterFace(me,lebon): + listf2v={} + #tri des faces connectees aux sommets selectionnes + for f in me.faces: + for v in f.v: + if v==lebon: + if v.index not in listf2v.keys(): + listf2v[me.verts.index(v)]=[f] + elif f not in listf2v[me.verts.index(v)]: + listf2v[me.verts.index(v)].append(f) + return listf2v + + +def creerFaceSupplementaire(me,lebon,listf2v): + global t + for f in listf2v[lebon.index]: + + f0=Blender.NMesh.Face() + if result==3: t=freeTampon(t) + + for v in f.v: + if result==3: t=Tampon(v,t) + + if v!=lebon: + f0.append(v) + else: + if result==2: + nv=Blender.NMesh.Vert(lebon.co[0]+Nr()*decal, + lebon.co[1]+Nr()*decal, + lebon.co[2]+Nr()*decal) + else: + nv=Blender.NMesh.Vert(lebon.co[0], + lebon.co[1], + lebon.co[2]) + nv.sel=1 + me.verts.append(nv) + + f0.append(me.verts[me.verts.index(nv)]) + localise=me.verts.index(nv) + docF(f0,f) + + if result==3: + t=TamponMoyen(t,f0.v) + me.verts[localise]=appliqueMoyenne(me.verts[localise],t) + me.faces.append(f0) + + del me.verts[me.verts.index(lebon)] + + for f in listf2v[lebon.index]: + del me.faces[me.faces.index(f)] + return me + +def collecte_edge(listf2v,me,lebon): + back=0 + edgelist = [] + vertlist = [] + if DEBUG : print listf2v + for face in listf2v[lebon.index]: + if len(face.v) == 4: + vlist = [0,1,2,3,0] + elif len(face.v) == 3: + vlist = [0,1,2,0] + else: + vlist = [0,1] + for i in range(len(vlist)-1): + vert0 = min(face.v[vlist[i]].index,face.v[vlist[i+1]].index) + vert1 = max(face.v[vlist[i]].index,face.v[vlist[i+1]].index) + edgeinlist = 0 + if vert0==lebon.index or vert1==lebon.index: + for edge in edgelist: + if ((edge[0]==vert0) and (edge[1]==vert1)): + edgeinlist = 1 + edge[2] = edge[2]+1 + edge.append(me.faces.index(face)) + break + if edgeinlist==0: + edge = [vert0,vert1,1,me.faces.index(face)] + edgelist.append(edge) + + for edge in edgelist: + #print edge + if len(edge)==4: + del edgelist[edgelist.index(edge)] + + edges=len(edgelist) + if DEBUG : print 'number of edges : ',edges," Edge list : " ,edgelist + return edges, edgelist + +MouseClickG= Blender.Draw.LEFTMOUSE +MouseClickD= Blender.Draw.RIGHTMOUSE +MouseClickM= Blender.Draw.MIDDLEMOUSE + +mouse_x=1 +mouse_y=1 + +x=1 +y=1 + +debut=0 + +def D(): + size=Buffer(GL_FLOAT, 4) + glGetFloatv(GL_SCISSOR_BOX, size) + size= size.list + glColor3f(0.1, 0.1, 0.15) + glRasterPos2f(10, size[3]-16) + Text("Quit = Q Key") + glRasterPos2f(10, size[3]-36) + Text("Mouse to the Right = Increase") + glRasterPos2f(10, size[3]-56) + Text("Mouse to the Left = Decrease") + +def E(evt,val): + global mouse_x,x,pl,orig,me,debut + global mouse_y,y, MouseClickG,MouseClickD,MouseClickM + if (evt== QKEY): Exit() + + if (evt == MOUSEX): + mouse_x = val + pos=x-mouse_x + x=mouse_x + if pos==0: + pos=1 + deplace(pl,orig,abs(pos)/pos) + + if (evt == MOUSEY): mouse_y = val + +def BE(evt): + pass + + +def deplace(pl,orig,n): + global me, OBJECT + for p in pl: + #print p, orig,len(me.verts) + me.verts[p].co[0]+=n*orig[0] + me.verts[p].co[1]+=n*orig[1] + me.verts[p].co[2]+=n*orig[2] + me.update() + Blender.Redraw() + + +def VertexBevel(result): + global t,pl, orig,me, SUBSURF + unique=0 + for v in me.verts: + if v.sel==1: + lebon=v + unique+=1 + + if unique==1: + edges=0 + edgelist=[] + vertlist=[] + orig=lebon.no[:] + listf2v=listConnecterFace(me,lebon) + edges, edgelist = collecte_edge(listf2v,me,lebon) + for f in listf2v[lebon.index]: + f0=Blender.NMesh.Face() + for v in f.v: + if v!=lebon: + f0.append(v) + else: + nv=Blender.NMesh.Vert(lebon.co[0],lebon.co[1],lebon.co[2]) + nv.sel=1 + me.verts.append(nv) + f0.append(me.verts[me.verts.index(nv)]) + for e in edgelist: + if e[-1]==me.faces.index(f) or e[-2]==me.faces.index(f): + if me.verts.index(nv) not in e: + e.insert(0,me.verts.index(nv)) + docF(f0,f) + me.faces.append(f0) + vertlist.append([me.verts.index(nv),me.faces.index(f)]) + for e in edgelist : + del e[e.index(lebon.index)] + f0=Blender.NMesh.Face() + for n in range(3): + f0.v.append(me.verts[e[n]]) + me.faces.append(f0); + + for ve in vertlist: + t=freeTampon(t) + for v in me.faces[ve[1]].v: + t=Tampon(v,t) + t=TamponMoyen(t,me.faces[ve[1]].v) + ve.append(t[:]) + me.verts[ve[0]]=appliqueMoyenne(me.verts[ve[0]],t) + + def swap(x,y): + return y,x + + p=[[edgelist[0][0],edgelist[0][1]]] + while len(p)!=len(edgelist): + for n in range(1,len(edgelist)) : + if p[-1][1]== edgelist[n][0]: + p.append([edgelist[n][0],edgelist[n][1]]) + n+=1 + elif p[-1][1]== edgelist[n][1]: + edgelist[n][0],edgelist[n][1]=swap(edgelist[n][0],edgelist[n][1]) + p.append([edgelist[n][0],edgelist[n][1]]) + n+=1 + if len(p)%2==0: + P0=p[:(len(p))/2] ; P1=p[len(p)/2:]; P1.reverse() + for s in range(len(P0)-1): + f0=Blender.NMesh.Face() + table=[P0[s][0],P0[s][1],P1[s+1][0],P1[s+1][1]] + for t in table:f0.v.append(me.verts[t]) + me.faces.append(f0) + elif len(p) >3 : + P0=p[:(len(p)-1)/2];P1=p[(len(p)-1)/2:-1]; P1.reverse() + for s in range(len(P0)-1): + f0=Blender.NMesh.Face() + table=[P0[s][0],P0[s][1],P1[s+1][0],P1[s+1][1]] + for t in table:f0.v.append(me.verts[t]) + me.faces.append(f0) + f0=Blender.NMesh.Face() + table=[p[-1][0],P0[0][0],P1[-1][1]] + for t in table:f0.v.append(me.verts[t]) + me.faces.append(f0) + + elif len(p) ==3 : + if DEBUG :print P0,P1 + f0=Blender.NMesh.Face() + table=[p[0][0],p[0][1],p[1][1]] + for t in table:f0.v.append(me.verts[t]) + me.faces.append(f0) + + for f in listf2v[lebon.index]: + del me.faces[me.faces.index(f)] + del me.verts[me.verts.index(lebon)] + me.update() + + if me.mode&Blender.NMesh.Modes['SUBSURF']: + me.mode-=Blender.NMesh.Modes['SUBSURF'] + SUBSURF=1 + me.update() + OBJECT[0].makeDisplayList() + + if result==5: + pl=[] + for s in me.verts: + if s.sel==1: + pl.append(s.index) + Blender.Draw.Register(D,E,BE) + + """ + if SUBSURF==1 : + me.mode+=Blender.NMesh.Modes['SUBSURF'] + me.update() + OBJECT[0].makeDisplayList() + """ + else: + name = " It could leave only one selected vertex %t | ok %x1 ?" + result = Blender.Draw.PupMenu(name) + +OBJECT=Blender.Object.GetSelected() + +if len(OBJECT)!=0: + if OBJECT[0].getType()=='Mesh': + name = "Unweld %t|Unbind Points %x1|With Noise %x2|Middle Face %x3|Static Bevel Vertex %x4|Moving Bevel Vertex %x5|" + result = Blender.Draw.PupMenu(name) + if result: + me=OBJECT[0].getData() + unique=0 + if result<4: + vSelection=[] + for v in me.verts: + if v.sel==1: + vSelection.append(v) + for v in vSelection: + lebon=v + if DEBUG : print lebon + listf2v=listConnecterFace(me,lebon) + me=creerFaceSupplementaire(me,lebon,listf2v) + #OBJECT[0].link(me) + me.update() + OBJECT[0].makeDisplayList() + else: + VertexBevel(result) + OBJECT[0].makeDisplayList() + + else: + name = "Nothing to do! Are you sure ?" + result = Blender.Draw.PupMenu(name) + + diff --git a/release/scripts/vrml97_export.py b/release/scripts/vrml97_export.py new file mode 100644 index 00000000000..7fb036f9997 --- /dev/null +++ b/release/scripts/vrml97_export.py @@ -0,0 +1,1065 @@ +#!BPY +""" Registration info for Blender menus: +Name: 'VRML97 (.wrl)...' +Blender: 235 +Group: 'Export' +Submenu: 'All Objects...' all +Submenu: 'Selected Objects...' selected +Tooltip: 'Export to VRML97 file format (.wrl)' +""" + +__author__ = ("Rick Kimball", "Ken Miller", "Steve Matthews", "Bart") +__url__ = ["blender", "elysiun", +"Author's (Rick) homepage, http://kimballsoftware.com/blender", +"Author's (Bart) homepage, http://www.neeneenee.de/vrml", +"Complete online documentation, http://www.neeneenee.de/blender/x3d/exporting_web3d.html"] +__version__ = "2005/04/09" + +__bpydoc__ = """\ +This script exports to VRML97 format, which used to be called VRML2. + +Usage: + +Run this script from "File->Export" menu. A pop-up will ask whether you +want to export only selected or all relevant objects. + +Known issues:<br> + Doesn't handle multiple materials (don't use material indices);<br> + Doesn't handle multiple UV textures on a single mesh (create a mesh +for each texture);<br> + Can't get the texture array associated with material * not the UV ones; +""" + + +# $Id$ +# +#------------------------------------------------------------------------ +# VRML97 exporter for blender 2.33 or above +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Copyright (C) 2003,2004: Rick Kimball rick@vrmlworld.net +# +# 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 ***** +# + +#################################### +# Library dependancies +#################################### + +import Blender +from Blender import Object, NMesh, Lamp, Draw, BGL, Image, Text +from Blender.Scene import Render +from os.path import exists, join +pytinst = 1 +import math + +#################################### +# Global Variables +#################################### + +scene = Blender.Scene.getCurrent() +world = Blender.World.Get() +worldmat = Blender.Texture.Get() +_safeOverwrite = True +radD=math.pi/180.0 +ARG='' + +def rad2deg(v): + return round(v*180.0/math.pi,4) + +def deg2rad(v): + return (v*math.pi)/180.0; + +class DrawTypes: + """Object DrawTypes enum values + BOUNDS - draw only the bounding box of the object + WIRE - draw object as a wire frame + SOLID - draw object with flat shading + SHADED - draw object with OpenGL shading +""" + BOUNDBOX = 1 + WIRE = 2 + SOLID = 3 + SHADED = 4 + TEXTURE = 5 + +if not hasattr(Blender.Object,'DrawTypes'): + Blender.Object.DrawTypes = DrawTypes() + +########################################################## +# Functions for writing output file +########################################################## + +class VRML2Export: + + def __init__(self, filename): + #--- public you can change these --- + self.matonly = 0 + self.share = 0 + self.billnode = 0 + self.halonode = 0 + self.collnode = 0 + self.tilenode = 0 + self.verbose=2 # level of verbosity in console 0-none, 1-some, 2-most + self.cp=3 # decimals for material color values 0.000 - 1.000 + self.vp=3 # decimals for vertex coordinate values 0.000 - n.000 + self.tp=3 # decimals for texture coordinate values 0.000 - 1.000 + self.it=3 + + #--- class private don't touch --- + self.texNames={} # dictionary of textureNames + self.matNames={} # dictionary of materiaNames + self.indentLevel=0 # keeps track of current indenting + self.filename=filename + self.file = open(filename, "w") + self.bNav=0 + self.nodeID=0 + self.namesReserved=[ "Anchor", "Appearance", "AudioClip", + "Background","Billboard", "Box", + "Collision", "Color", "ColorInterpolator", "Cone", "Coordinate", "CoordinateInterpolator", "Cylinder", "CylinderSensor", + "DirectionalLight", + "ElevationGrid", "Extrustion", + "Fog", "FontStyle", "Group", + "ImageTexture", "IndexedFaceSet", "IndexedLineSet", "Inline", + "LOD", "Material", "MovieTexture", + "NavigationInfo", "Normal", "NormalInterpolator","OrientationInterpolator", + "PixelTexture", "PlaneSensor", "PointLight", "PointSet", "PositionInterpolator", "ProxmimitySensor", + "ScalarInterpolator", "Script", "Shape", "Sound", "Sphere", "SphereSensor", "SpotLight", "Switch", + "Text", "TextureCoordinate", "TextureTransform", "TimeSensor", "TouchSensor", "Transform", + "Viewpoint", "VisibilitySensor", "WorldInfo" ] + self.namesStandard=[ "Empty","Empty.000","Empty.001","Empty.002","Empty.003","Empty.004","Empty.005", + "Empty.006","Empty.007","Empty.008","Empty.009","Empty.010","Empty.011","Empty.012", + "Scene.001","Scene.002","Scene.003","Scene.004","Scene.005","Scene.06","Scene.013", + "Scene.006","Scene.007","Scene.008","Scene.009","Scene.010","Scene.011","Scene.012", + "World","World.000","World.001","World.002","World.003","World.004","World.005" ] + +########################################################## +# Writing nodes routines +########################################################## + + def writeHeader(self): + self.file.write("#VRML V2.0 utf8\n\n") + self.file.write("# This file was authored with Blender (http://www.blender.org/)\n") + self.file.write("# Exported using VRML97 exporter v1.50\n\n") + + def writeInline(self): + inlines = Blender.Scene.Get() + allinlines = len(inlines) + if scene != inlines[0]: + return + else: + for i in range(allinlines): + nameinline=inlines[i].getName() + if (nameinline not in self.namesStandard) and (i > 0): + self.writeIndented("DEF %s Inline {\n" % (self.cleanStr(nameinline)), 1) + nameinline = nameinline+".wrl" + self.writeIndented("url \"%s\" \n" % nameinline) + self.writeIndented("}\n", -1) + self.writeIndented("\n") + + def writeScript(self): + textEditor = Blender.Text.Get() + alltext = len(textEditor) + for i in range(alltext): + nametext = textEditor[i].getName() + nlines = textEditor[i].getNLines() + if (nametext == "web3d" or nametext == "web3d.js" or nametext == "web3d.txt") and (nlines != None): + nalllines = len(textEditor[i].asLines()) + alllines = textEditor[i].asLines() + for j in range(nalllines): + self.writeIndented(alllines[j] + "\n") + self.writeIndented("\n") + + def writeViewpoint(self, thisObj): + context = scene.getRenderingContext() + ratio = float(context.imageSizeY())/float(context.imageSizeX()) + lens = (360* (math.atan(ratio *16 / thisObj.data.getLens()) / 3.141593))*(3.141593/180) + if lens > 3.14: + lens = 3.14 + self.writeIndented("DEF %s Viewpoint {\n" % (self.cleanStr(thisObj.name)), 1) + self.writeIndented("description \"%s\" \n" % (thisObj.name)) + # get the camera location, subtract 90 degress from X to orient like VRML does + loc = self.rotatePointForVRML(thisObj.loc) + rot = [thisObj.RotX - 1.57, thisObj.RotY, thisObj.RotZ] + nRot = self.rotatePointForVRML(rot) + # convert to Quaternion and to Angle Axis + Q = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2]) + Q1 = self.multiplyQuaternions(Q[0], Q[1]) + Qf = self.multiplyQuaternions(Q1, Q[2]) + angleAxis = self.quaternionToAngleAxis(Qf) + # write orientation statement + self.writeIndented("orientation %3.2f %3.2f %3.2f %3.2f\n" % (angleAxis[0], angleAxis[1], -angleAxis[2], angleAxis[3])) + # write position statement + self.writeIndented("position %3.2f %3.2f %3.2f\n" % (loc[0], loc[1], loc[2])) + self.writeIndented("fieldOfView %.3f\n" % (lens)) + self.writeIndented("}\n", -1) + self.writeIndented("\n") + + def writeFog(self): + if len(world) > 0: + mtype = world[0].getMistype() + mparam = world[0].getMist() + grd = world[0].getHor() + grd0, grd1, grd2 = grd[0], grd[1], grd[2] + else: + return + if (mtype == 1): + self.writeIndented("Fog {\n",1) + self.writeIndented("fogType \"LINEAR\"\n") + self.writeIndented("color %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + "\n") + self.writeIndented("visibilityRange " + str(round(mparam[2],self.cp)) + "\n") + self.writeIndented("}\n",-1) + self.writeIndented("\n") + elif (mtype == 2): + self.writeIndented("Fog {\n",1) + self.writeIndented("fogType \"EXPONENTIAL\"\n") + self.writeIndented("color %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + "\n") + self.writeIndented("visibilityRange " + str(round(mparam[2],self.cp)) + "\n") + self.writeIndented("}\n",-1) + self.writeIndented("\n") + else: + return + + def writeNavigationInfo(self, scene): + allObj = [] + allObj = scene.getChildren() + headlight = "TRUE" + vislimit = 0.0 + for thisObj in allObj: + objType=thisObj.getType() + if objType == "Camera": + vislimit = thisObj.data.getClipEnd() + elif objType == "Lamp": + headlight = "FALSE" + self.writeIndented("NavigationInfo {\n",1) + self.writeIndented("headlight %s" % headlight + "\n") + self.writeIndented("visibilityLimit %s\n" % (round(vislimit,self.cp))) + self.writeIndented("type [\"EXAMINE\", \"ANY\"]\n") + self.writeIndented("avatarSize [0.25, 1.75, 0.75]\n") + self.writeIndented("} \n",-1) + self.writeIndented(" \n") + + def writeSpotLight(self, object, lamp): + safeName = self.cleanStr(object.name) + if len(world) > 0: + ambi = world[0].getAmb() + ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 + else: + ambi = 0 + ambientIntensity = 0 + + # compute cutoff and beamwidth + intensity=min(lamp.energy/1.5,1.0) + beamWidth=deg2rad(lamp.spotSize)*.37; + cutOffAngle=beamWidth*1.3 + + (dx,dy,dz)=self.computeDirection(object) + # note -dx seems to equal om[3][0] + # note -dz seems to equal om[3][1] + # note dy seems to equal om[3][2] + om = object.getMatrix() + + location=self.rotVertex(om, (0,0,0)); + radius = lamp.dist*math.cos(beamWidth) + self.writeIndented("DEF %s SpotLight {\n" % safeName,1) + self.writeIndented("radius %s\n" % (round(radius,self.cp))) + self.writeIndented("ambientIntensity %s\n" % (round(ambientIntensity,self.cp))) + self.writeIndented("intensity %s\n" % (round(intensity,self.cp))) + self.writeIndented("color %s %s %s\n" % (round(lamp.col[0],self.cp), + round(lamp.col[1],self.cp), + round(lamp.col[2],self.cp))) + self.writeIndented("beamWidth %s\n" % (round(beamWidth,self.cp))) + self.writeIndented("cutOffAngle %s\n" % (round(cutOffAngle,self.cp))) + self.writeIndented("direction %s %s %s\n" % (round(dx,3),round(dy,3),round(dz,3))) + self.writeIndented("location %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3))) + self.writeIndented("}\n",-1) + self.writeIndented("\n") + + def writeDirectionalLight(self, object, lamp): + safeName = self.cleanStr(object.name) + if len(world) > 0: + ambi = world[0].getAmb() + ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 + else: + ambi = 0 + ambientIntensity = 0 + + intensity=min(lamp.energy/1.5, 1.0) + (dx,dy,dz)=self.computeDirection(object) + self.writeIndented("DEF %s DirectionalLight {\n" % safeName,1) + self.writeIndented("ambientIntensity %s\n" % (round(ambientIntensity,self.cp))) + self.writeIndented("color %s %s %s\n" % (round(lamp.col[0],self.cp), + round(lamp.col[1],self.cp), + round(lamp.col[2],self.cp))) + self.writeIndented("intensity %s\n" % (round(intensity,self.cp))) + self.writeIndented("direction %s %s %s\n" % (round(dx,4),round(dy,4),round(dz,4))) + self.writeIndented("}\n",-1) + self.writeIndented("\n") + + def writePointLight(self, object, lamp): + safeName = self.cleanStr(object.name) + if len(world) > 0: + ambi = world[0].getAmb() + ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5 + else: + ambi = 0 + ambientIntensity = 0 + om = object.getMatrix() + location=self.rotVertex(om, (0,0,0)); + intensity=min(lamp.energy/1.5,1.0) + radius = lamp.dist + self.writeIndented("DEF %s PointLight {\n" % safeName,1) + self.writeIndented("ambientIntensity %s\n" % (round(ambientIntensity,self.cp))) + self.writeIndented("color %s %s %s\n" % (round(lamp.col[0],self.cp), + round(lamp.col[1],self.cp), + round(lamp.col[2],self.cp))) + self.writeIndented("intensity %s\n" % (round(intensity,self.cp))) + self.writeIndented("location %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3))) + self.writeIndented("radius %s\n" % radius ) + self.writeIndented("}\n",-1) + self.writeIndented("\n") + + def writeNode(self, thisObj): + objectname=str(thisObj.getName()) + if objectname in self.namesStandard: + return + else: + (dx,dy,dz)=self.computeDirection(thisObj) + om = thisObj.getMatrix() + location=self.rotVertex(om, (0,0,0)); + self.writeIndented("%s {\n" % objectname,1) + self.writeIndented("# direction %s %s %s\n" % (round(dx,3),round(dy,3),round(dz,3))) + self.writeIndented("# location %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3))) + self.writeIndented("}\n",-1) + self.writeIndented("\n") + def createDef(self, name): + name = name + str(self.nodeID) + self.nodeID=self.nodeID+1 + if len(name) <= 3: + newname = "_" + str(self.nodeID) + return "%s" % (newname) + else: + for bad in [' ','"','#',"'",',','.','[','\\',']','{','}']: + name=name.replace(bad,'_') + if name in self.namesReserved: + newname = name[0:3] + "_" + str(self.nodeID) + return "%s" % (newname) + elif name[0].isdigit(): + newname = "_" + name + str(self.nodeID) + return "%s" % (newname) + else: + newname = name + return "%s" % (newname) + + def secureName(self, name): + name = name + str(self.nodeID) + self.nodeID=self.nodeID+1 + if len(name) <= 3: + newname = "_" + str(self.nodeID) + return "%s" % (newname) + else: + for bad in ['"','#',"'",',','.','[','\\',']','{','}']: + name=name.replace(bad,'_') + if name in self.namesReserved: + newname = name[0:3] + "_" + str(self.nodeID) + return "%s" % (newname) + elif name[0].isdigit(): + newname = "_" + name + str(self.nodeID) + return "%s" % (newname) + else: + newname = name + return "%s" % (newname) + + def writeIndexedFaceSet(self, object, normals = 0): + + imageMap={} # set of used images + sided={} # 'one':cnt , 'two':cnt + vColors={} # 'multi':1 + meshName = self.cleanStr(object.name) + mesh=object.getData() + for face in mesh.faces: + if face.mode & Blender.NMesh.FaceModes['HALO'] and self.halonode == 0: + self.writeIndented("Billboard {\n",1) + self.writeIndented("axisOfRotation 0 0 0\n") + self.writeIndented("children [\n") + self.halonode = 1 + elif face.mode & Blender.NMesh.FaceModes['BILLBOARD'] and self.billnode == 0: + self.writeIndented("Billboard {\n",1) + self.writeIndented("axisOfRotation 0 1 0\n") + self.writeIndented("children [\n") + self.billnode = 1 + elif face.mode & Blender.NMesh.FaceModes['OBCOL'] and self.matonly == 0: + self.matonly = 1 + elif face.mode & Blender.NMesh.FaceModes['SHAREDCOL'] and self.share == 0: + self.share = 1 + elif face.mode & Blender.NMesh.FaceModes['TILES'] and self.tilenode == 0: + self.tilenode = 1 + elif not face.mode & Blender.NMesh.FaceModes['DYNAMIC'] and self.collnode == 0: + self.writeIndented("Collision {\n",1) + self.writeIndented("collide FALSE\n") + self.writeIndented("children [\n") + self.collnode = 1 + + nIFSCnt=self.countIFSSetsNeeded(mesh, imageMap, sided, vColors) + + if nIFSCnt > 1: + self.writeIndented("DEF %s%s Group {\n" % ("G_", meshName),1) + self.writeIndented("children [\n",1) + + if sided.has_key('two') and sided['two'] > 0: + bTwoSided=1 + else: + bTwoSided=0 + om = object.getMatrix(); + location=self.rotVertex(om, (0,0,0)); + self.writeIndented("Transform {\n",1) + self.writeIndented("translation %s %s %s\n" % (round(location[0],3), + round(location[1],3), + round(location[2],3)),1) + self.writeIndented("children [\n") + self.writeIndented("DEF %s Shape {\n" % meshName,1) + + maters=mesh.materials + hasImageTexture=0 + issmooth=0 + + if len(maters) > 0 or mesh.hasFaceUV(): + self.writeIndented("appearance 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) + self.writeMaterial(mat, self.createDef(maters[0].name)) + if len(maters) > 1: + print "Warning: mesh named %s has multiple materials" % meshName + print "Warning: only one material per object handled" + else: + self.writeIndented("material NULL\n") + + #-- textures + if mesh.hasFaceUV(): + for face in mesh.faces: + if (hasImageTexture == 0) and (face.image): + self.writeImageTexture(face.image.name) + hasImageTexture=1 # keep track of face texture + if self.tilenode == 1: + self.writeIndented("textureTransform TextureTransform { scale %s %s }\n" % (face.image.xrep, face.image.yrep)) + self.tilenode = 0 + self.writeIndented("}\n", -1) + + #-- IndexedFaceSet or IndexedLineSet + + # check if object is wireframe only + if object.drawType == Blender.Object.DrawTypes.WIRE: + # user selected WIRE=2 on the Drawtype=Wire on (F9) Edit page + ifStyle="IndexedLineSet" + else: + # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5 + ifStyle="IndexedFaceSet" + + + self.writeIndented("geometry %s {\n" % ifStyle, 1) + if object.drawType != Blender.Object.DrawTypes.WIRE: + if bTwoSided == 1: + self.writeIndented("solid FALSE\n") + else: + self.writeIndented("solid TRUE\n") + + #--- output coordinates + self.writeCoordinates(object, mesh, meshName) + + if object.drawType != Blender.Object.DrawTypes.WIRE: + #--- output textureCoordinates if UV texture used + if mesh.hasFaceUV(): + if hasImageTexture == 1: + self.writeTextureCoordinates(mesh) + elif self.matonly == 1 and self.share == 1: + self.writeFaceColors(mesh) + + for face in mesh.faces: + if face.smooth: + issmooth=1 + if issmooth==1: + creaseAngle=(mesh.getMaxSmoothAngle())*radD + self.writeIndented("creaseAngle %s\n" % (round(creaseAngle,self.cp))) + + #--- output vertexColors + if self.share == 1 and self.matonly == 0: + self.writeVertexColors(mesh) + self.matonly = 0 + self.share = 0 + #--- output closing braces + self.writeIndented("}\n", -1) + self.writeIndented("}\n", -1) + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + + if self.halonode == 1: + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + self.halonode = 0 + + if self.billnode == 1: + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + self.billnode = 0 + + if self.collnode == 1: + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + self.collnode = 0 + + if nIFSCnt > 1: + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + + self.writeIndented("\n") + + def writeCoordinates(self, object, mesh, meshName): + #-- vertices + self.writeIndented("coord DEF %s%s Coordinate {\n" % ("coord_",meshName), 1) + self.writeIndented("point [\n\t\t\t\t\t\t", 1) + meshVertexList = mesh.verts + + # create vertex list and pre rotate -90 degrees X for VRML + mm=object.getMatrix() + location=self.rotVertex(mm, (0,0,0)); + for vertex in meshVertexList: + v=self.rotVertex(mm, vertex); + self.file.write("%s %s %s, " % + (round((v[0]-location[0]),self.vp), + round((v[1]-location[1]),self.vp), + round((v[2]-location[2]),self.vp) )) + self.writeIndented("\n", 0) + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + + self.writeIndented("coordIndex [\n\t\t\t\t\t", 1) + coordIndexList=[] + for face in mesh.faces: + cordStr="" + for i in range(len(face)): + indx=meshVertexList.index(face[i]) + cordStr = cordStr + "%s " % indx + self.file.write(cordStr + "-1, ") + self.writeIndented("\n", 0) + self.writeIndented("]\n", -1) + + def writeTextureCoordinates(self, mesh): + texCoordList=[] + texIndexList=[] + j=0 + + for face in mesh.faces: + for i in range(len(face)): + texIndexList.append(j) + texCoordList.append(face.uv[i]) + j=j+1 + texIndexList.append(-1) + + self.writeIndented("texCoord TextureCoordinate {\n", 1) + self.writeIndented("point [\n\t\t\t\t\t\t", 1) + for i in range(len(texCoordList)): + self.file.write("%s %s, " % + (round(texCoordList[i][0],self.tp), + round(texCoordList[i][1],self.tp))) + self.writeIndented("\n", 0) + self.writeIndented("]\n", -1) + self.writeIndented("}\n", -1) + + self.writeIndented("texCoordIndex [\n\t\t\t\t\t\t", 1) + texIndxStr="" + for i in range(len(texIndexList)): + texIndxStr = texIndxStr + "%d, " % texIndexList[i] + if texIndexList[i]==-1: + self.file.write(texIndxStr) + texIndxStr="" + self.writeIndented("\n", 0) + self.writeIndented("]\n", -1) + + def writeFaceColors(self, mesh): + self.writeIndented("colorPerVertex FALSE\n") + self.writeIndented("color Color {\n",1) + self.writeIndented("color [\n\t\t\t\t\t\t", 1) + + for face in mesh.faces: + if face.col: + c=face.col[0] + if self.verbose > 2: + print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b) + + aColor = self.rgbToFS(c) + self.file.write("%s, " % aColor) + self.writeIndented("\n", 0) + self.writeIndented("]\n",-1) + self.writeIndented("}\n",-1) + + def writeVertexColors(self, mesh): + self.writeIndented("colorPerVertex TRUE\n") + self.writeIndented("color Color {\n",1) + self.writeIndented("color [\n\t\t\t\t\t\t", 1) + + for i in range(len(mesh.verts)): + c=self.getVertexColorByIndx(mesh,i) + if self.verbose > 2: + print "Debug: vertex[%d].col r=%d g=%d b=%d" % (i, c.r, c.g, c.b) + + aColor = self.rgbToFS(c) + self.file.write("%s, " % aColor) + self.writeIndented("\n", 0) + self.writeIndented("]\n",-1) + self.writeIndented("}\n",-1) + + def writeMaterial(self, mat, matName): + # look up material name, use it if available + if self.matNames.has_key(matName): + self.writeIndented("material USE %s\n" % matName) + self.matNames[matName]+=1 + return; + + self.matNames[matName]=1 + + ambient = mat.amb/2 + diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2] + if len(world) > 0: + ambi = world[0].getAmb() + ambi0, ambi1, ambi2 = ambi[0], ambi[1], ambi[2] + else: + ambi = 0 + ambi0, ambi1, ambi2 = 0, 0, 0 + emisR, emisG, emisB = (diffuseR*mat.emit+ambi0)/4, (diffuseG*mat.emit+ambi1)/4, (diffuseB*mat.emit+ambi2)/4 + + shininess = mat.hard/255.0 + specR = (mat.specCol[0]+0.001)/(1.05/(mat.getSpec()+0.001)) + specG = (mat.specCol[1]+0.001)/(1.05/(mat.getSpec()+0.001)) + specB = (mat.specCol[2]+0.001)/(1.05/(mat.getSpec()+0.001)) + transp = 1-mat.alpha + + self.writeIndented("material DEF %s Material {\n" % matName, 1) + self.writeIndented("diffuseColor %s %s %s" % + (round(diffuseR,self.cp), round(diffuseG,self.cp), round(diffuseB,self.cp)) + + "\n") + self.writeIndented("ambientIntensity %s" % + (round(ambient,self.cp))+ + "\n") + self.writeIndented("specularColor %s %s %s" % + (round(specR,self.cp), round(specG,self.cp), round(specB,self.cp)) + + "\n" ) + self.writeIndented("emissiveColor %s %s %s" % + (round(emisR,self.cp), round(emisG,self.cp), round(emisB,self.cp)) + + "\n" ) + self.writeIndented("shininess %s" % + (round(shininess,self.cp)) + + "\n" ) + self.writeIndented("transparency %s" % + (round(transp,self.cp)) + + "\n") + self.writeIndented("}\n",-1) + + def writeImageTexture(self, name): + if self.texNames.has_key(name): + self.writeIndented("texture USE %s\n" % self.cleanStr(name)) + self.texNames[name] += 1 + return + else: + self.writeIndented("texture DEF %s ImageTexture {\n" % self.cleanStr(name), 1) + self.writeIndented("url \"%s\"\n" % name) + self.writeIndented("}\n",-1) + self.texNames[name] = 1 + + def writeBackground(self): + if len(world) > 0: + worldname = world[0].getName() + else: + return + blending = world[0].getSkytype() + grd = world[0].getHor() + grd0, grd1, grd2 = grd[0], grd[1], grd[2] + sky = world[0].getZen() + sky0, sky1, sky2 = sky[0], sky[1], sky[2] + mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2] + mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2 + if worldname in self.namesStandard: + self.writeIndented("Background {\n",1) + else: + self.writeIndented("DEF %s Background {\n" % self.createDef(worldname),1) + # No Skytype - just Hor color + if blending == 0: + self.writeIndented("groundColor %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + "\n") + self.writeIndented("skyColor %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + "\n") + # Blend Gradient + elif blending == 1: + self.writeIndented("groundColor [ %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + ",") + self.writeIndented("%s %s %s" %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)) + " ]\n") + self.writeIndented("groundAngle [ 1.57, 1.57 ]\n") + self.writeIndented("skyColor [ %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + ",") + self.writeIndented("%s %s %s" %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)) + " ]\n") + self.writeIndented("skyAngle [ 1.57, 1.57 ]\n") + # Blend+Real Gradient Inverse + elif blending == 3: + self.writeIndented("groundColor [ %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + ", ") + self.writeIndented("%s %s %s" %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)) + " ]\n") + self.writeIndented("groundAngle [ 1.57, 1.57 ]\n") + self.writeIndented("skyColor [ %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + ", ") + self.writeIndented("%s %s %s" %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)) + " ]\n") + self.writeIndented("skyAngle [ 1.57, 1.57 ]\n") + # Paper - just Zen Color + elif blending == 4: + self.writeIndented("groundColor %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + "\n") + self.writeIndented("skyColor %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + "\n") + # Blend+Real+Paper - komplex gradient + elif blending == 7: + self.writeIndented("groundColor [ %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + ", ") + self.writeIndented("%s %s %s" %(round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + " ]\n") + self.writeIndented("groundAngle [ 1.57, 1.57 ]\n") + self.writeIndented("skyColor [ %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + ",") + self.writeIndented("%s %s %s" %(round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + " ]\n") + self.writeIndented("skyAngle [ 1.57, 1.57 ]\n") + # Any Other two colors + else: + self.writeIndented("groundColor %s %s %s" % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)) + "\n") + self.writeIndented("skyColor %s %s %s" % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)) + "\n") + alltexture = len(worldmat) + for i in range(alltexture): + namemat = worldmat[i].getName() + pic = worldmat[i].getImage() + if (namemat == "back") and (pic != None): + self.writeIndented("backUrl \"" + str(pic.getName()) + "\"\n") + elif (namemat == "bottom") and (pic != None): + self.writeIndented("bottomUrl \"" + str(pic.getName()) + "\"\n") + elif (namemat == "front") and (pic != None): + self.writeIndented("frontUrl \"" + str(pic.getName()) + "\"\n") + elif (namemat == "left") and (pic != None): + self.writeIndented("leftUrl \"" + str(pic.getName()) + "\"\n") + elif (namemat == "right") and (pic != None): + self.writeIndented("rightUrl \"" + str(pic.getName()) + "\"\n") + elif (namemat == "top") and (pic != None): + self.writeIndented("topUrl \"" + str(pic.getName()) + "\"\n") + self.writeIndented("}",-1) + self.writeIndented("\n\n") + +########################################################## +# export routine +########################################################## + + def export(self, scene, world, worldmat): + print "Info: starting VRML97 export to " + self.filename + "..." + self.writeHeader() + self.writeNavigationInfo(scene) + self.writeBackground() + self.writeFog() + allObj = [] + if ARG == 'selected': + allObj = Blender.Object.GetSelected() + else: + allObj = scene.getChildren() + self.writeInline() + for thisObj in allObj: + try: + objType=thisObj.getType() + objName=thisObj.getName() + self.matonly = 0 + if objType == "Camera": + self.writeViewpoint(thisObj) + elif objType == "Mesh": + self.writeIndexedFaceSet(thisObj, normals = 0) + elif objType == "Lamp": + lmpName=Lamp.Get(thisObj.data.getName()) + lmpType=lmpName.getType() + if lmpType == Lamp.Types.Lamp: + self.writePointLight(thisObj, lmpName) + elif lmpType == Lamp.Types.Spot: + self.writeSpotLight(thisObj, lmpName) + elif lmpType == Lamp.Types.Sun: + self.writeDirectionalLight(thisObj, lmpName) + else: + self.writeDirectionalLight(thisObj, lmpName) + elif objType == "Empty" and objName != "Empty": + self.writeNode(thisObj) + else: + #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType()) + print "" + except AttributeError: + print "Error: Unable to get type info for %s" % thisObj.getName() + if ARG != 'selected': + self.writeScript() + self.cleanup() + +########################################################## +# Utility methods +########################################################## + + def cleanup(self): + self.file.close() + self.texNames={} + self.matNames={} + self.indentLevel=0 + print "Info: finished VRML97 export to %s\n" % self.filename + + def cleanStr(self, name, prefix='rsvd_'): + """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name""" + + newName=name[:] + if len(newName) == 0: + self.nNodeID+=1 + return "%s%d" % (prefix, self.nNodeID) + + if newName in self.namesReserved: + newName='%s%s' % (prefix,newName) + + if newName[0].isdigit(): + newName='%s%s' % ('_',newName) + + for bad in [' ','"','#',"'",',','.','[','\\',']','{','}']: + newName=newName.replace(bad,'_') + return newName + + def countIFSSetsNeeded(self, mesh, imageMap, sided, vColors): + """ + countIFFSetsNeeded() - should look at a blender mesh to determine + how many VRML IndexFaceSets or IndexLineSets are needed. A + new mesh created under the following conditions: + + o - split by UV Textures / one per mesh + o - split by face, one sided and two sided + o - split by smooth and flat faces + o - split when faces only have 2 vertices * needs to be an IndexLineSet + """ + + imageNameMap={} + faceMap={} + nFaceIndx=0 + + for face in mesh.faces: + sidename=''; + if (face.mode & NMesh.FaceModes.TWOSIDE) == NMesh.FaceModes.TWOSIDE: + sidename='two' + else: + sidename='one' + + if not vColors.has_key('multi'): + for face in mesh.faces: + if face.col: + c=face.col[0] + if c.r != 255 and c.g != 255 and c.b !=255: + vColors['multi']=1 + + if sided.has_key(sidename): + sided[sidename]+=1 + else: + sided[sidename]=1 + + if face.image: + faceName="%s_%s" % (face.image.name, sidename); + + if imageMap.has_key(faceName): + imageMap[faceName].append(face) + else: + imageMap[faceName]=[face.image.name,sidename,face] + + if self.verbose > 2: + for faceName in imageMap.keys(): + ifs=imageMap[faceName] + print "Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \ + (faceName, ifs[0], ifs[1], len(ifs)-2) + + return len(imageMap.keys()) + + def faceToString(self,face): + + print "Debug: face.flag=0x%x (bitflags)" % face.flag + if face.flag & NMesh.FaceFlags.SELECT == NMesh.FaceFlags.SELECT: + print "Debug: face.flag.SELECT=true" + + print "Debug: face.mode=0x%x (bitflags)" % face.mode + if (face.mode & NMesh.FaceModes.TWOSIDE) == NMesh.FaceModes.TWOSIDE: + print "Debug: face.mode twosided" + + print "Debug: face.transp=0x%x (enum)" % face.transp + if face.transp == NMesh.FaceTranspModes.SOLID: + print "Debug: face.transp.SOLID" + + if face.image: + print "Debug: face.image=%s" % face.image.name + print "Debug: face.materialIndex=%d" % face.materialIndex + + def getVertexColorByIndx(self, mesh, indx): + for face in mesh.faces: + j=0 + for vertex in face.v: + if vertex.index == indx: + c=face.col[j] + j=j+1 + return c + + def meshToString(self,mesh): + print "Debug: mesh.hasVertexUV=%d" % mesh.hasVertexUV() + print "Debug: mesh.hasFaceUV=%d" % mesh.hasFaceUV() + print "Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours() + print "Debug: mesh.verts=%d" % len(mesh.verts) + print "Debug: mesh.faces=%d" % len(mesh.faces) + print "Debug: mesh.materials=%d" % len(mesh.materials) + + def rgbToFS(self, c): + s="%s %s %s" % ( + round(c.r/255.0,self.cp), + round(c.g/255.0,self.cp), + round(c.b/255.0,self.cp)) + return s + + def computeDirection(self, object): + x,y,z=(0,-1.0,0) # point down + ax,ay,az = (object.RotX,object.RotZ,object.RotY) + + # rot X + x1=x + y1=y*math.cos(ax)-z*math.sin(ax) + z1=y*math.sin(ax)+z*math.cos(ax) + + # rot Y + x2=x1*math.cos(ay)+z1*math.sin(ay) + y2=y1 + z2=z1*math.cos(ay)-x1*math.sin(ay) + + # rot Z + x3=x2*math.cos(az)-y2*math.sin(az) + y3=x2*math.sin(az)+y2*math.cos(az) + z3=z2 + + return [x3,y3,z3] + + + # swap Y and Z to handle axis difference between Blender and VRML + #------------------------------------------------------------------------ + def rotatePointForVRML(self, v): + x = v[0] + y = v[2] + z = -v[1] + + vrmlPoint=[x, y, z] + return vrmlPoint + + def rotVertex(self, mm, v): + lx,ly,lz=v[0],v[1],v[2] + gx=(mm[0][0]*lx + mm[1][0]*ly + mm[2][0]*lz) + mm[3][0] + gy=((mm[0][2]*lx + mm[1][2]*ly+ mm[2][2]*lz) + mm[3][2]) + gz=-((mm[0][1]*lx + mm[1][1]*ly + mm[2][1]*lz) + mm[3][1]) + rotatedv=[gx,gy,gz] + return rotatedv + + # For writing well formed VRML code + #------------------------------------------------------------------------ + def writeIndented(self, s, inc=0): + if inc < 1: + self.indentLevel = self.indentLevel + inc + + spaces="" + for x in xrange(self.indentLevel): + spaces = spaces + "\t" + self.file.write(spaces + s) + + if inc > 0: + self.indentLevel = self.indentLevel + inc + + # Converts a Euler to three new Quaternions + # Angles of Euler are passed in as radians + #------------------------------------------------------------------------ + def eulerToQuaternions(self, x, y, z): + Qx = [math.cos(x/2), math.sin(x/2), 0, 0] + Qy = [math.cos(y/2), 0, math.sin(y/2), 0] + Qz = [math.cos(z/2), 0, 0, math.sin(z/2)] + + quaternionVec=[Qx,Qy,Qz] + return quaternionVec + + # Multiply two Quaternions together to get a new Quaternion + #------------------------------------------------------------------------ + def multiplyQuaternions(self, Q1, Q2): + result = [((Q1[0] * Q2[0]) - (Q1[1] * Q2[1]) - (Q1[2] * Q2[2]) - (Q1[3] * Q2[3])), + ((Q1[0] * Q2[1]) + (Q1[1] * Q2[0]) + (Q1[2] * Q2[3]) - (Q1[3] * Q2[2])), + ((Q1[0] * Q2[2]) + (Q1[2] * Q2[0]) + (Q1[3] * Q2[1]) - (Q1[1] * Q2[3])), + ((Q1[0] * Q2[3]) + (Q1[3] * Q2[0]) + (Q1[1] * Q2[2]) - (Q1[2] * Q2[1]))] + + return result + + # Convert a Quaternion to an Angle Axis (ax, ay, az, angle) + # angle is in radians + #------------------------------------------------------------------------ + def quaternionToAngleAxis(self, Qf): + scale = math.pow(Qf[1],2) + math.pow(Qf[2],2) + math.pow(Qf[3],2) + ax = Qf[1] + ay = Qf[2] + az = Qf[3] + + if scale > .0001: + ax/=scale + ay/=scale + az/=scale + + angle = 2 * math.acos(Qf[0]) + + result = [ax, ay, az, angle] + return result + +########################################################## +# Callbacks, needed before Main +########################################################## + +def select_file(filename): + if pytinst == 1: + if exists(filename) and _safeOverwrite: + result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0") + if(result != 1): + return + + if not filename.endswith('.wrl'): filename += '.wrl' + wrlexport=VRML2Export(filename) + wrlexport.export(scene, world, worldmat) + +def createWRLPath(): + filename = Blender.Get('filename') + #print filename + + if filename.find('.') != -1: + filename = filename.split('.')[0] + filename += ".wrl" + #print filename + + return filename + +######################################################### +# main routine +######################################################### + +try: + ARG = __script__['arg'] # user selected argument +except: + print "older version" + +if Blender.Get('version') < 235: + print "Warning: VRML97 export failed, wrong blender version!" + print " You aren't running blender version 2.35 or greater" + print " download a newer version from http://blender.org/" +else: + Blender.Window.FileSelector(select_file,"Export VRML97",createWRLPath()) |