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.
This commit is contained in:
Willian Padovani Germano 2005-04-16 05:25:42 +00:00
parent 8b664b924b
commit d65fc84a68
30 changed files with 4619 additions and 1240 deletions

@ -1,121 +0,0 @@
Version 3.233-2004
******************
Espanol
Sale del programa
Utilidades de...%t|Alinea objetos%x1|Creacion%x2|Edita mallas%x3|Edita objetos%x4
11
Mov
Esc
Encaja
Abarca
Separa
Alinea
Rota
Incr.
Crea nuevos objetos
Es+
Es*
Separar entre:%t|Origenes%x1|Centros geometricos%x2|Minimos%x3|Maximos%x4|Baricentro%x5|Objetos%x6
Crear%t|Arco (3 ptos.)%x1|Arco (interactivo)%x2|Circunferencia (3 ptos.)%x3
12
Puntos
Centro
Orden
Objeto
AngIni:
AngFin:
Angulo:
Radio:
Puntos:
Centro
Nombre:
Puntos
Modifica vertices%t|Subdivide%x1|Envia a un plano%x2|Aplica LocRotSize%x3
Partes
Proyectar en el plano:%t|Coordenado global...%x1|Coordenado local...%x2
Actuar sobre el plano%t|Yz%x1|Zx%x2|Xy%x3
En la dirección%t|X%x1|Y%x2|Z%x3|Ortogonal al plano%x4
Captura
Buffer%t|Copia vector diferencia%x1|Copia distancia%x2|Copia diferencia de rotacion%x3|Copia media LocRotSiz%x4|Ver buffer en consola%x5
Transformar LocRotSize%t|Hacia el obj. activo%x1|Aleatoriamente%x2
Poner a distancia fija%x1|Sumar (desp. absoluto)%x2|Multiplicar (desp. relativo)%x3
********************
English
Exit program
Utils about:%t|Align Objects%x1|Create%x2|Edit Meshes%x3|Edit Objects%x4
11
Mov
Sca
Fit
Embrace
Separate
Align
Rota
Incr.
Create new objects
Sc+
Sc*
Separate between:%t|Origins%x1|Geometric centers%x2|Minimum%x3|Maximum%x4|Baricenter%x5|Objects%x6
Create what%t|Arc (3 pts.)%x1|Arc (interactive)%x2|Circunference (3 pts.)%x3
12
Points
Centre
Sort
Object
AngIni:
AngEnd:
Angle:
Radius:
Points:
Centre
ObjName:
Points
Modify vertices%t|Subdivide edges%x1|Send to a plane%x2|Set LocRotSize%x3
Parts
Project onto the plane:%t|Global coordinated...%x1|Local coordinated...%x2
Act on plane%t|Yz%x1|Zx%x2|Xy%x3
In direction%t|X%x1|Y%x2|Z%x3|Ortogonal to plane%x4
Get
Buffer%t|Copy diference vector%x1|Copy distance%x2|Copy rot diference%x3|Copy LocRotSiz average%x4|Show Buffer in Console%x5
Transform LocRotSize%t|Close to active%x1|Randomly%x2
Set at fixed distance%x1|Add (absolute displ.)%x2|Multiply (relative displ.)%x3
********************
Catala
Surt del programa
Utilitats de...%t|Alinea objectes%x1|Creacio%x2|Edita malles%x3|Edita objetes%x4
11
Mov
Esc
Encaixa
Abarca
Separa
Alinea
Rotacio
Incr.
Crea objectes nous
Es+
Es*
Separa entra:%t|Origens%x1|Centres geometrics%x2|Minims%x3|Maxims%x4|Baricentre%x5|Objectes%x6
Crear%t|Arc (3 pts.)%x1|Arc (interactiu)%x2|Circumferencia (3 pts.)%x3
12
Punts
Centre
Ordre
Objecte
AngIni:
AngFi:
Angle:
Radi:
Punts:
Centre
Nom:
Punts
Modifica vertex%t|Subdivideix%x1|Envia a un pla%x2|Aplica LocRotSize%x3
Parts
Projectar en el pla:%t|Coordenacio global...%x1|Coordenacio local...%x2
Actuar sobre el pla%t|Yz%x1|Zx%x2|Xy%x3
En la direccio%t|X%x1|Y%x2|Z%x3|Ortogonal al pla%x4
Captura
Buffer%t|Copia vector diferencia%x1|Copia distancia%x2|Copia diferencia de rotacio%x3|Copia mitjana LocRotSiz%x4|Veure buffer en consola%x5
Transformar LocRotSize%t|Cap al obj. actiu%x1|Aleatoriamente%x2
Posar a distancia fixa%x1|Sumar (desp. absolut)%x2|Multiplicar (desp. relatiu)%x3

@ -1,9 +0,0 @@
This directory is the default place for scripts to put their data,
like internal files needed by the script and its saved configuration.
Scripts can find the path to this dir using Blender.Get("datadir").
Ex:
import Blender
print Blender.Get("datadir")

@ -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 *****
#
@ -65,29 +88,49 @@ more than one texture image assigned, check TuxKart's wiki.
#
# 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
ARG = __script__['arg'] # user selected argument
# Globals
ERROR_MSG = '' # popup error msg
REPORT_DATA = {
'main': [],
'errors': [],
'warns': [],
'nosplit': [],
'noexport': []
}
TOKENS_DONT_EXPORT = ['!', '#']
TOKENS_DONT_SPLIT = ['=', '$']
MATIDX_ERROR = False
HELPME = 0 # help window
REG_KEY = 'ac3d_export'
SKIP_DATA = 1
MIRCOL_AS_AMB = 0
MIRCOL_AS_EMIS = 0
ADD_DEFAULT_MAT = 1
# 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 = ''
# Looking for a saved key in Blender.Registry dict:
rd = Blender.Registry.GetKey('AC3DExport')
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']
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)",
}
def update_RegistryInfo():
d = {}
@ -95,14 +138,52 @@ def update_RegistryInfo():
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)
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)
# Looking for a saved key in Blender.Registry dict:
rd = Blender.Registry.GetKey(REG_KEY, True)
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()
else:
update_RegistryInfo()
VERBOSE = True
CONFIRM_OVERWRITE = True
# check General scripts config key for default behaviors
rd = Blender.Registry.GetKey('General', True)
if rd:
try:
VERBOSE = rd['verbose']
CONFIRM_OVERWRITE = rd['confirm_overwrite']
except: pass
# 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
@ -112,118 +193,263 @@ def Round(f):
return str(r)
def transform_verts(verts, m):
r = []
vecs = []
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
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:
def __init__(self, scene, filename):
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
print 'Trying AC3DExport...'
MATIDX_ERROR = 0
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'
world_kids = 0
kids_dict = self.kids_dict = {}
objs = []
exp_objs = self.exp_objs = []
tree = {}
try:
file = open(filename, 'w')
file = self.file = open(filename, 'w')
except IOError, (errno, strerror):
errmsg = "IOError #%s" % errno
errmsg = errmsg + "%t|" + strerror
Blender.Draw.PupMenu(errmsg)
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(buf)
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)
print "Done. Saved to %s\n" % 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 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 = ''
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
@ -233,48 +459,62 @@ class AC3DExport:
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]))
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"
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))
mbuf = mbuf + "%s %s %s %s %s %s %s\n" \
% (material, rgb, amb, emis, spec, shi, trans)
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 = self.mbuf + mbuf
self.mbuf += "".join(mbuf)
def OBJECT(self, type):
self.buf = self.buf + "OBJECT %s\n" % type
self.file.write('OBJECT %s\n' % type)
def name(self, name):
self.buf = self.buf + 'name "%s"\n' % 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 data(self, name):
self.buf = self.buf + 'data %s\n%s\n' % (len(name), 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 = []
tex = ""
for f in faces:
if f.image and f.image.name not in tex:
tex.append(f.image.name)
if f.image:
tex = f.image.name
break
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
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.buf = self.buf + buf
self.file.write(buf)
def rot(self, matrix):
rot = ''
@ -286,35 +526,38 @@ class AC3DExport:
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
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]:
buf = 'loc %s %s %s\n' % (loc[0], loc[1], loc[2])
self.buf = self.buf + buf
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):
buf = "numvert %s\n" % len(verts)
m = matrix_mul(matrix)
file = self.file
file.write("numvert %s\n" % len(verts))
m = matrix * acmatrix
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
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):
def numsurf(self, faces, hasFaceUV, foomesh = False):
global ADD_DEFAULT_MAT
global ADD_DEFAULT_MAT, MATIDX_ERROR
file = self.file
buf = "numsurf %s\n" % len(faces)
file.write("numsurf %s\n" % len(faces))
if not foomesh: verts = self.mesh.verts
mlist = self.mlist
indexerror = 0
omlist = {}
objmats = self.mesh.materials
objmats = self.mesh.materials[:]
matidx_error_told = 0
for i in range(len(objmats)):
objmats[i] = objmats[i].name
for f in faces:
@ -322,151 +565,120 @@ class AC3DExport:
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
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
buf = buf + "SURF 0x%d%d\n" % (flaghigh, flaglow)
surfstr = "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
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:
vindex = self.mesh.verts.index(vert)
fvstr.append(str(vert.index))
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
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))
def kids(self, kids = 0):
self.buf = self.buf + "kids %s\n" % kids
fvstr = "".join(fvstr)
file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr))
# End of Class AC3DExport
from Blender import Draw, BGL
from Blender.Window import FileSelector
def gui():
global SKIP_DATA, MIRCOL_AS_AMB, MIRCOL_AS_EMIS, ADD_DEFAULT_MAT, HELPME
global HELPME
def report_data():
global VERBOSE
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.")
if not VERBOSE: return
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)
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):
scene = Blender.Scene.GetCurrent()
test = AC3DExport(scene, filename)
global ERROR_MSG, EXPORT_DIR, OBJS, CONFIRM_OVERWRITE, VERBOSE
if __script__['arg'] == 'config':
Draw.Register(gui, event, b_event)
if not filename.endswith('.ac'): filename = '%s.ac' % filename
if bsys.exists(filename) and CONFIRM_OVERWRITE:
if Blender.Draw.PupMenu('OVERWRITE?%t|File exists') != 1:
return
Blender.Window.WaitCursor(1)
starttime = bsys.time()
export_dir = bsys.dirname(filename)
if export_dir != EXPORT_DIR:
EXPORT_DIR = export_dir
update_RegistryInfo()
test = AC3DExport(OBJS, filename)
if ERROR_MSG:
Blender.Draw.PupMenu(ERROR_MSG)
ERROR_MSG = ''
else:
fname = Blender.sys.makename(ext=".ac")
Blender.Window.FileSelector(fs_callback, "Export AC3D", fname)
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 not OBJS:
Blender.Draw.PupMenu('ERROR: No objects selected')
else:
fname = bsys.makename(ext=".ac")
if EXPORT_DIR:
fname = bsys.join(EXPORT_DIR, bsys.basename(fname))
FileSelector(fs_callback, "Export AC3D", fname)

@ -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
@ -68,22 +74,57 @@ textures, for the case of wrong paths in the .ac file(s).
# 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 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']
if TEXDIR:
TEXDIR = TEXDIR.replace('\\','/')
if TEXDIR[-1] != '/': TEXDIR += '/'
errmsg = ""
def inform(msg):
global VERBOSE
if VERBOSE: print msg
class Obj:
def __init__(self, type):
@ -96,6 +137,7 @@ class Obj:
self.texoff = None
self.loc = [0, 0, 0]
self.rot = []
self.crease = 30
self.vlist = []
self.flist = []
self.matlist = []
@ -107,8 +149,6 @@ class AC3DImport:
global errmsg
print "Trying to import AC3D model(s) from %s ..." % filename
self.i = 0
errmsg = ''
self.importdir = Blender.sys.dirname(filename)
@ -116,18 +156,20 @@ class AC3DImport:
file = open(filename, 'r')
except IOError, (errno, strerror):
errmsg = "IOError #%s: %s" % (errno, strerror)
print errmsg
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 = 'Invalid file -- AC3D header not found.'
print errmsg
errmsg = 'AC3D header not found (invalid file)'
Blender.Draw.PupMenu('ERROR: %s' % errmsg)
inform(errmsg)
return None
elif version != 'b':
print 'AC3D file version 0x%s.' % version
print 'This importer is for version 0xb, so it may fail.'
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,
@ -140,7 +182,8 @@ class AC3DImport:
'MATERIAL': self.parse_mat,
'texture': self.parse_tex,
'texrep': self.parse_texrep,
'texoff': self.parse_texoff}
'texoff': self.parse_texoff,
'crease': self.parse_crease}
self.objlist = []
self.mlist = []
@ -203,16 +246,22 @@ class AC3DImport:
lines = self.lines
line = lines[i].split()
mat_name = ''
mat_col = mat_spec_col = [0,0,0]
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_spec_col, 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()
@ -230,6 +279,14 @@ class AC3DImport:
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
@ -360,8 +417,11 @@ class AC3DImport:
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]
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:
@ -380,7 +440,8 @@ class AC3DImport:
obj.kids -= 1
continue
mesh = Blender.NMesh.New()
if (obj.data): mesh.name = obj.data
if obj.data: mesh.name = obj.data
mesh.setMaxSmoothAngle(obj.crease) # will clamp to [1, 80]
mesh.hasFaceUV(1)
tex = None
@ -397,10 +458,10 @@ class AC3DImport:
tex = Blender.Image.Load(obj.tex)
except:
try:
obj.tex = TEXDIR + basetexname
obj.tex = TEXTURES_DIR + basetexname
tex = Blender.Image.Load(obj.tex)
except:
print "Couldn't load texture: %s" % basetexname
inform("Couldn't load texture: %s" % basetexname)
for v in obj.vlist:
bvert = Blender.NMesh.Vert(v[0],v[1],v[2])
@ -447,11 +508,16 @@ class AC3DImport:
dadobj = Blender.Object.get(self.dads.pop())
dadobj.makeParent([object])
print '...done!'
# End of class AC3DImport
def filesel_callback(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")

@ -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

@ -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'
]

@ -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()

792
release/scripts/config.py Normal file

@ -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)

@ -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)

@ -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):
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
if (browse_scrollstart<0): browse_scrollstart= 0
elif (browse_scrollstart>=len(browselist)): browse_scrollstart= len(browselist)-1
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)

@ -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]
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()

@ -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')

@ -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,13 +98,39 @@ 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'):
dirlist = [
['homedir', 'Blender home dir', 1],
['scriptsdir', 'Default dir for scripts', 1],
['datadir', 'Default "bpydata/" data dir for scripts', 1],
['uscriptsdir', 'User defined dir for scripts', 0],
['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")
else:
notices += 1
output.write(" <NOTICE> -- not found")
else:
output.write(" %s" % dirpath)
has_dir[dirname] = True
if not has_dir['homedir']:
outmsg = """
<WARNING> - Blender home dir not found!
@ -121,55 +148,14 @@ if not Blender.Get('homedir'):
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],
['datadir', 'Default "bpydata/" data dir for scripts', 1],
['uscriptsdir', 'User defined dir for scripts', 0],
['udatadir', 'Data dir "bpydata/" inside user defined dir', 0]
]
for dir in dirlist:
dirname, dirstr, is_critical = dir
dirpath = Blender.Get(dirname)
output.write("\n\n %s:\n" % dirstr)
if not dirpath:
if is_critical:
warnings += 1
output.write(" <WARNING> -- not found")
else:
notices += 1
output.write(" <NOTICE> -- not found")
else:
output.write(" %s" % dirpath)
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 Blender.Get('udatadir'):
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")

439
release/scripts/unweld.py Normal file

@ -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)

File diff suppressed because it is too large Load Diff

@ -94,6 +94,8 @@ static int bpymenu_group_atoi( char *str )
return PYMENU_HELPSYSTEM;
else if( !strcmp( str, "Render" ) )
return PYMENU_RENDER;
else if( !strcmp( str, "System" ) )
return PYMENU_SYSTEM;
else if( !strcmp( str, "Object" ) )
return PYMENU_OBJECT;
else if( !strcmp( str, "Mesh" ) )
@ -141,6 +143,9 @@ char *BPyMenu_group_itoa( short menugroup )
case PYMENU_RENDER:
return "Render";
break;
case PYMENU_SYSTEM:
return "System";
break;
case PYMENU_OBJECT:
return "Object";
break;

@ -25,7 +25,7 @@
*
* This is a new part of Blender.
*
* Contributor(s): Willian P. Germano
* Contributor(s): Willian P. Germano, Matt Ebb
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
@ -87,6 +87,7 @@ typedef enum {
PYMENU_MISC,
PYMENU_OBJECT,
PYMENU_RENDER,/* exporters to external renderers */
PYMENU_SYSTEM,
PYMENU_THEMES,
PYMENU_UV,/* UV editing tools, to go in UV/Image editor space, 'UV' menu */
PYMENU_WIZARDS,/* complex 'app' scripts */

@ -24,7 +24,8 @@
*
* This is a new part of Blender.
*
* Contributor(s): Michel Selten, Willian P. Germano, Joseph Gilbert
* Contributor(s): Michel Selten, Willian P. Germano, Joseph Gilbert,
* Campbell Barton
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
@ -66,6 +67,8 @@
#include "../BPY_extern.h" /* BPY_txt_do_python_Text */
#include "../BPY_menus.h" /* to update menus */
extern PyObject *bpy_registryDict; /* defined in ../BPY_interface.c */
/**********************************************************/
/* Python API function prototypes for the Blender module. */
/**********************************************************/
@ -76,6 +79,7 @@ static PyObject *Blender_Quit( PyObject * self );
static PyObject *Blender_Load( PyObject * self, PyObject * args );
static PyObject *Blender_Save( PyObject * self, PyObject * args );
static PyObject *Blender_Run( PyObject * self, PyObject * args );
static PyObject *Blender_ShowHelp( PyObject * self, PyObject * args );
static PyObject *Blender_UpdateMenus( PyObject * self);
extern PyObject *Text3d_Init( void ); /* missing in some include */
@ -137,6 +141,15 @@ static char Blender_Run_doc[] =
"(script) - Run the given Python script.\n\
(script) - the path to a file or the name of an available Blender Text.";
static char Blender_ShowHelp_doc[] =
"(script) - Show help for the given Python script.\n\
This will try to open the 'Scripts Help Browser' script, so to have\n\
any help displayed the passed 'script' must be properly documented\n\
with the expected strings (check API ref docs or any bundled script\n\
for examples).\n\n\
(script) - the filename of a script in the default or user defined\n\
scripts dir (no need to supply the full path name).";
static char Blender_UpdateMenus_doc[] =
"() - Update the menus where scripts are registered. Only needed for\n\
scripts that save other new scripts in the default or user defined folders.";
@ -152,6 +165,7 @@ static struct PyMethodDef Blender_methods[] = {
{"Load", Blender_Load, METH_VARARGS, Blender_Load_doc},
{"Save", Blender_Save, METH_VARARGS, Blender_Save_doc},
{"Run", Blender_Run, METH_VARARGS, Blender_Run_doc},
{"ShowHelp", Blender_ShowHelp, METH_VARARGS, Blender_ShowHelp_doc},
{"UpdateMenus", ( PyCFunction ) Blender_UpdateMenus, METH_NOARGS,
Blender_UpdateMenus_doc},
{NULL, NULL, 0, NULL}
@ -272,6 +286,110 @@ static PyObject *Blender_Get( PyObject * self, PyObject * args )
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* USER PREFS: */
else if( StringEqual( str, "yfexportdir" ) ) {
if (U.yfexportdir[0] != '\0') {
char yfexportdir[FILE_MAXDIR];
BLI_strncpy(yfexportdir, U.yfexportdir, FILE_MAXDIR);
BLI_convertstringcode(yfexportdir, G.sce, 0);
if( BLI_exists( yfexportdir ) )
ret = PyString_FromString( yfexportdir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* fontsdir */
else if( StringEqual( str, "fontsdir" ) ) {
if (U.fontdir[0] != '\0') {
char fontdir[FILE_MAXDIR];
BLI_strncpy(fontdir, U.fontdir, FILE_MAXDIR);
BLI_convertstringcode(fontdir, G.sce, 0);
if( BLI_exists( fontdir ) )
ret = PyString_FromString( fontdir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* texturesdir */
else if( StringEqual( str, "texturesdir" ) ) {
if (U.textudir[0] != '\0') {
char textudir[FILE_MAXDIR];
BLI_strncpy(textudir, U.textudir, FILE_MAXDIR);
BLI_convertstringcode(textudir, G.sce, 0);
if( BLI_exists( textudir ) )
ret = PyString_FromString( textudir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* texpluginsdir */
else if( StringEqual( str, "texpluginsdir" ) ) {
if (U.plugtexdir[0] != '\0') {
char plugtexdir[FILE_MAXDIR];
BLI_strncpy(plugtexdir, U.plugtexdir, FILE_MAXDIR);
BLI_convertstringcode(plugtexdir, G.sce, 0);
if( BLI_exists( plugtexdir ) )
ret = PyString_FromString( plugtexdir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* seqpluginsdir */
else if( StringEqual( str, "seqpluginsdir" ) ) {
if (U.plugseqdir[0] != '\0') {
char plugseqdir[FILE_MAXDIR];
BLI_strncpy(plugseqdir, U.plugseqdir, FILE_MAXDIR);
BLI_convertstringcode(plugseqdir, G.sce, 0);
if( BLI_exists( plugseqdir ) )
ret = PyString_FromString( plugseqdir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* renderdir */
else if( StringEqual( str, "renderdir" ) ) {
if (U.renderdir[0] != '\0') {
char renderdir[FILE_MAXDIR];
BLI_strncpy(renderdir, U.renderdir, FILE_MAXDIR);
BLI_convertstringcode(renderdir, G.sce, 0);
if( BLI_exists( renderdir ) )
ret = PyString_FromString( renderdir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* soundsdir */
else if( StringEqual( str, "soundsdir" ) ) {
if (U.sounddir[0] != '\0') {
char sounddir[FILE_MAXDIR];
BLI_strncpy(sounddir, U.sounddir, FILE_MAXDIR);
BLI_convertstringcode(sounddir, G.sce, 0);
if( BLI_exists( sounddir ) )
ret = PyString_FromString( sounddir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* tempdir */
else if( StringEqual( str, "tempdir" ) ) {
if (U.tempdir[0] != '\0') {
char tempdir[FILE_MAXDIR];
BLI_strncpy(tempdir, U.tempdir, FILE_MAXDIR);
BLI_convertstringcode(tempdir, G.sce, 0);
if( BLI_exists( tempdir ) )
ret = PyString_FromString( tempdir );
}
if (!ret) ret = EXPP_incr_ret(Py_None);
}
/* According to the old file (opy_blender.c), the following if
statement is a quick hack and needs some clean up. */
else if( StringEqual( str, "vrmloptions" ) ) {
@ -493,11 +611,56 @@ static PyObject *Blender_Save( PyObject * self, PyObject * args )
return Py_None;
}
static PyObject *Blender_ShowHelp(PyObject *self, PyObject *args)
{
PyObject *script = NULL;
char hspath[FILE_MAXDIR + FILE_MAXFILE]; /* path to help_browser.py */
char *sdir = bpy_gethome(1);
PyObject *rkeyd = NULL, *arglist = NULL;
if (!PyArg_ParseTuple(args, "O!", &PyString_Type, &script))
return EXPP_ReturnPyObjError(PyExc_TypeError,
"expected a script filename as argument");
/* first we try to find the help_browser script */
if (sdir) BLI_make_file_string("/", hspath, sdir, "help_browser.py");
if (!sdir || (!BLI_exists(hspath) && (U.pythondir[0] != '\0'))) {
char upydir[FILE_MAXDIR];
BLI_strncpy(upydir, U.pythondir, FILE_MAXDIR);
BLI_convertstringcode(upydir, G.sce, 0);
BLI_make_file_string("/", hspath, upydir, "help_browser.py");
if (!BLI_exists(hspath))
return EXPP_ReturnPyObjError(PyExc_RuntimeError,
"can't find script help_browser.py");
}
/* now we store the passed script in the registry dict and call the
* help_browser to show help info for it */
rkeyd = PyDict_New();
if (!rkeyd)
return EXPP_ReturnPyObjError(PyExc_MemoryError,
"can't create py dictionary!");
PyDict_SetItemString(rkeyd, "script", script);
PyDict_SetItemString(bpy_registryDict, "__help_browser", rkeyd);
arglist = Py_BuildValue("(s)", hspath);
Blender_Run(self, arglist);
Py_DECREF(arglist);
return EXPP_incr_ret(Py_None);
}
static PyObject *Blender_Run(PyObject *self, PyObject *args)
{
char *fname = NULL;
Text *text = NULL;
int is_blender_text = 0;
Script *script = NULL;
if (!PyArg_ParseTuple(args, "s", &fname))
return EXPP_ReturnPyObjError(PyExc_TypeError,
@ -527,7 +690,28 @@ static PyObject *Blender_Run(PyObject *self, PyObject *args)
}
}
BPY_txt_do_python_Text(text);
/* (this is messy, check Draw.c's Method_Register and Window.c's file
* selector for more info)
* - caller script is the one that called this Blender_Run function;
* - called script is the argument to this function: fname;
* To mark scripts whose global dicts can't be freed right after
* the script execution (or better, 'first pass', since these scripts
* leave callbacks for gui or file/image selectors) we flag them. But to
* get a pointer to them we need to check which one is currently
* running (if none we're already at a spacescript). To make sure only
* the called script will have the SCRIPT_RUNNING flag on, we unset it
* for the caller script here: */
script = G.main->script.first;
while (script) {
if (script->flags & SCRIPT_RUNNING) break;
script = script->id.next;
}
if (script) script->flags &= ~SCRIPT_RUNNING; /* unset */
BPY_txt_do_python_Text(text); /* call new script */
if (script) script->flags |= SCRIPT_RUNNING; /* set */
if (!is_blender_text) free_libblock(&G.main->text, text);

@ -65,6 +65,7 @@ char *bpy_gethome(int append_scriptsdir)
{
static char homedir[FILE_MAXDIR];
static char scriptsdir[FILE_MAXDIR];
char tmpdir[FILE_MAXDIR];
char bprogdir[FILE_MAXDIR];
char *s;
int i;
@ -91,20 +92,37 @@ char *bpy_gethome(int append_scriptsdir)
}
else return homedir;
}
else homedir[0] = '\0';
/* otherwise, use argv[0] (bprogname) to get .blender/ in
/* if either:
* no homedir was found or
* append_scriptsdir = 1 but there's no scripts/ inside homedir,
* use argv[0] (bprogname) to get .blender/ in
* Blender's installation dir */
s = BLI_last_slash( bprogname );
i = s - bprogname + 1;
PyOS_snprintf( bprogdir, i, bprogname );
BLI_make_file_string( "/", homedir, bprogdir, ".blender" );
PyOS_snprintf( bprogdir, i, "%s", bprogname );
if (BLI_exists(homedir)) {
/* using tmpdir to preserve homedir (if) found above:
* the ideal is to have a home dir with scripts dir inside
* it, but if that isn't available, it's possible to
* have a 'broken' home dir somewhere and a scripts dir in the
* cvs sources */
BLI_make_file_string( "/", tmpdir, bprogdir, ".blender" );
if (BLI_exists(tmpdir)) {
if (append_scriptsdir) {
BLI_make_file_string("/", scriptsdir, homedir, "scripts");
if (BLI_exists(scriptsdir)) return scriptsdir;
BLI_make_file_string("/", scriptsdir, tmpdir, "scripts");
if (BLI_exists(scriptsdir)) {
PyOS_snprintf(homedir, FILE_MAXDIR, "%s", tmpdir);
return scriptsdir;
}
else {
homedir[0] = '\0';
scriptsdir[0] = '\0';
}
}
else return homedir;
}
@ -113,6 +131,7 @@ char *bpy_gethome(int append_scriptsdir)
if (append_scriptsdir) {
BLI_make_file_string("/", scriptsdir, bprogdir, "release/scripts");
if (BLI_exists(scriptsdir)) return scriptsdir;
else scriptsdir[0] = '\0';
}
return NULL;

@ -2824,6 +2824,11 @@ static PyObject *M_NMesh_PutRaw( PyObject * self, PyObject * args )
if( !during_script( ) )
EXPP_allqueue( REDRAWVIEW3D, 0 );
if (ob && G.obedit) { /* prevents a crash when a new object is created */
exit_editmode(1);
enter_editmode();
}
// @OK...this requires some explanation:
// Materials can be assigned two ways:
// a) to the object data (in this case, the mesh)

@ -33,11 +33,11 @@
#include "Registry.h"
#include <stdio.h>
#include <BKE_global.h> /* G.f & G_DEBUG */
#include "gen_utils.h"
/* the Registry dictionary */
PyObject *bpy_registryDict = NULL;
@ -64,17 +64,23 @@ char M_Registry_Keys_doc[] =
Each key references another dict with saved data from a specific script.\n";
char M_Registry_GetKey_doc[] =
"(name) - Get a specific entry (dict) from the Registry dictionary\n\
(name) - a string that references a specific script.\n";
"(name, disk = False) - Get an entry (a dict) from the Registry dictionary\n\
(name) - a string that references a specific script;\n\
(disk = False) - search on the user (if available) or default scripts config\n\
data dir.\n";
char M_Registry_SetKey_doc[] =
"(key, dict) - Store an entry in the Registry dictionary.\n\
"(key, dict, disk = False) - Store an entry in the Registry dictionary.\n\
If an entry with the same 'key' already exists, it is substituted.\n\
(key) - the string to use as a key for the dict being saved.\n\
(dict) - a dictionary with the data to be stored.\n";
(dict) - a dictionary with the data to be stored.\n\
(disk = False) - also write data as a config file inside the user (if\n\
available) or default scripts config data dir.\n";
char M_Registry_RemoveKey_doc[] =
"(key) - Remove the dict with key 'key' from the Registry.\n";
"(key, disk = False) - Remove the dict with key 'key' from the Registry.\n\
(key) - the name of the key to delete;\n\
(disk = False) - if True the respective config file is also deleted.\n";
/*****************************************************************************/
/* Python method structure definition for Blender.Registry module: */
@ -118,25 +124,35 @@ static PyObject *M_Registry_GetKey( PyObject * self, PyObject * args )
{
PyObject *pyentry = NULL;
PyObject *pydict = NULL;
int disk = 0;
if( !bpy_registryDict )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"No Registry dictionary found!" );
if( !PyArg_ParseTuple( args, "O!", &PyString_Type, &pyentry ) )
if( !PyArg_ParseTuple( args, "O!|i", &PyString_Type, &pyentry, &disk ) )
return EXPP_ReturnPyObjError( PyExc_AttributeError,
"expected a string" );
"expected a string and optionally a bool" );
pydict = PyDict_GetItem( bpy_registryDict, pyentry ); /* borrowed ... */
if( !pydict )
/* return EXPP_ReturnPyObjError (PyExc_KeyError,
"no such key in the Registry"); */
pydict = Py_None; /* better to return None than an error */
if (!pydict) {
if (disk > 0) {
/* try to get data from disk */
char buf[256];
PyOS_snprintf(buf, sizeof(buf),
"import Blender, BPyRegistry; BPyRegistry.LoadConfigData('%s')",
PyString_AsString(pyentry));
if (!PyRun_SimpleString(buf))
pydict = PyDict_GetItem(bpy_registryDict, pyentry);
else PyErr_Clear();
}
Py_INCREF( pydict ); /* ... so we incref it */
/* should we copy the dict instead? */
return pydict;
if (!pydict) /* no need to return a KeyError, since without doubt */
pydict = Py_None; /* Py_None means no key (all valid keys are dicts) */
}
return EXPP_incr_ret (pydict); /* ... so we incref it */
}
/*****************************************************************************/
@ -147,14 +163,15 @@ static PyObject *M_Registry_SetKey( PyObject * self, PyObject * args )
{
PyObject *pystr = NULL;
PyObject *pydict = NULL;
int disk = 0;
if( !bpy_registryDict )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"No Registry dictionary found!" );
if( !PyArg_ParseTuple( args, "O!O!",
if( !PyArg_ParseTuple( args, "O!O!|i",
&PyString_Type, &pystr, &PyDict_Type,
&pydict ) )
&pydict, &disk ) )
return EXPP_ReturnPyObjError( PyExc_AttributeError,
"expected a string and a dictionary" );
@ -162,6 +179,19 @@ static PyObject *M_Registry_SetKey( PyObject * self, PyObject * args )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"Registry_SetKey: couldn't update the Registry dict" );
if (disk) {
/* try to save data to disk */
char buf[256];
PyOS_snprintf(buf, sizeof(buf),
"import Blender, BPyRegistry; BPyRegistry.SaveConfigData('%s')",
PyString_AsString(pystr));
if (PyRun_SimpleString(buf) != 0) {
PyErr_Clear();
if (G.f & G_DEBUG)
fprintf(stderr, "\nCan't save script configuration data!\n");
}
}
Py_INCREF( Py_None );
return Py_None;
}
@ -173,18 +203,31 @@ static PyObject *M_Registry_SetKey( PyObject * self, PyObject * args )
static PyObject *M_Registry_RemoveKey( PyObject * self, PyObject * args )
{
PyObject *pystr = NULL;
int disk = 0;
if( !bpy_registryDict )
return EXPP_ReturnPyObjError( PyExc_RuntimeError,
"No Registry dictionary found!" );
if( !PyArg_ParseTuple( args, "O!", &PyString_Type, &pystr ) )
if( !PyArg_ParseTuple( args, "O!|i", &PyString_Type, &pystr, &disk ) )
return EXPP_ReturnPyObjError( PyExc_AttributeError,
"expected a string" );
"expected a string and optionally a bool" );
if( PyDict_DelItem( bpy_registryDict, pystr ) ) /* returns 0 on success */
return EXPP_ReturnPyObjError( PyExc_KeyError,
"no such key in the Registry" );
else if (disk) {
/* try to delete from disk too */
char buf[256];
PyOS_snprintf(buf, sizeof(buf),
"import Blender, BPyRegistry; BPyRegistry.RemoveConfigData('%s')",
PyString_AsString(pystr));
if (PyRun_SimpleString(buf) != 0) {
PyErr_Clear();
if (G.f & G_DEBUG)
fprintf(stderr, "\nCan't remove script configuration data file!\n");
}
}
Py_INCREF( Py_None );
return Py_None;

@ -25,13 +25,14 @@
*
* This is a new part of Blender.
*
* Contributor(s): Willian P. Germano
* Contributor(s): Willian P. Germano, Campbell Barton
*
* ***** END GPL/BL DUAL LICENSE BLOCK *****
*/
#include <BKE_utildefines.h>
#include <BLI_blenlib.h>
#include <DNA_scene_types.h> /* G.scene->r.cfra */
#include <PIL_time.h>
#include <Python.h>
#include <sys/stat.h>
@ -51,6 +52,7 @@ static PyObject *M_sys_makename( PyObject * self, PyObject * args,
static PyObject *M_sys_exists( PyObject * self, PyObject * args );
static PyObject *M_sys_time( PyObject * self );
static PyObject *M_sys_sleep( PyObject * self, PyObject * args );
static PyObject *M_sys_expandpath( PyObject *self, PyObject *args);
/*****************************************************************************/
/* The following string definitions are used for documentation strings. */
@ -108,6 +110,15 @@ The return value is as follows:\n\
\t 2: path is an existing dirname;\n\
\t-1: path exists but is neither a regular file nor a dir.";
static char M_sys_expandpath_doc[] =
"(path) - Expand this Blender internal path to a proper file system path.\n\
(path) - the string path to convert.\n\n\
Note: internally Blender paths can contain two special character sequences:\n\
- '//' (at start) for base path directory (the current .blend's dir path);\n\
- '#' (at ending) for current frame number.\n\n\
This function expands these to their actual content, returning a valid path.\n\
If the special chars are not found in the given path, it is simply returned.";
/*****************************************************************************/
/* Python method structure definition for Blender.sys module: */
/*****************************************************************************/
@ -122,6 +133,7 @@ struct PyMethodDef M_sys_methods[] = {
{"exists", M_sys_exists, METH_VARARGS, M_sys_exists_doc},
{"sleep", M_sys_sleep, METH_VARARGS, M_sys_sleep_doc},
{"time", ( PyCFunction ) M_sys_time, METH_NOARGS, M_sys_time_doc},
{"expandpath", M_sys_expandpath, METH_VARARGS, M_sys_expandpath_doc},
{NULL, NULL, 0, NULL}
};
@ -395,3 +407,18 @@ static PyObject *M_sys_exists( PyObject * self, PyObject * args )
return Py_BuildValue( "i", i );
}
static PyObject *M_sys_expandpath( PyObject * self, PyObject * args )
{
char *path = NULL;
char expanded[FILE_MAXDIR + FILE_MAXFILE];
if (!PyArg_ParseTuple( args, "s", &path))
return EXPP_ReturnPyObjError( PyExc_TypeError,
"expected string argument" );
BLI_strncpy(expanded, path, FILE_MAXDIR + FILE_MAXFILE);
BLI_convertstringcode(expanded, G.sce, G.scene->r.cfra);
return PyString_FromString(expanded);
}

@ -40,9 +40,16 @@ The Blender Python API Reference
- L{Texture}
- L{Types}
- L{Window}
- L{Theme} (new)
- L{Theme}
- L{World}
- L{sys<Sys>}
- L{sys<Sys>} (*)
Additional information:
-----------------------
- L{Misc facilities<API_related>}:
- scripts: registering in menus, documenting, configuring (new);
- command line examples (new).
(*) - marks updated.
@ -91,57 +98,6 @@ These are the basic ways to execute scripts in Blender:
6. A script can call another script (that will run in its own context, with
its own global dictionary) with the L{Blender.Run} module function.
Registering scripts:
--------------------
To be registered a script needs two things:
- be either in the default scripts dir or in the user defined scripts path
(see Info window, paths tab);
- have a proper header.
Try 'blender -d' to know where your default dir for scripts is, it will
inform either the dir or the file with that info already parsed, which is
in the same dir of the scripts folder.
The header should be like this one (all double and single apostrophes below
are required)::
#!BPY
# \"\"\"
# Name: 'Script Name'
# Blender: 233
# Group: 'Export'
# Submenu: 'All' all
# Submenu: 'Selected' sel
# Submenu: 'Configure (gui)' gui
# Tooltip: 'Export to some format.'
# \"\"\"
where:
- B{Name} is the string that will appear in the menu;
- B{Blender} is the minimum program version required to run the script;
- B{Group} defines where the script will be put, see all groups in the
Scripts Window's header, menu "Scripts";
- B{Submenu} adds optional submenus for further control;
- B{Tooltip} is the (short) tooltip string for the menu entry.
note:
- all double and single apostrophes above are required;
- B{*NEW*}: you can "comment out" the header above, by starting lines with
'#', like we did. This is not required (except for the first line, #!BPY,
of course), but this way the header won't conflict with Python tools that
you can use to generate documentation for your script code. Just
remember to keep this header above any other line with triple
double-quotes (\"\"\") in your script.
Submenu lines are not required, use them if you want to provide extra
options. To see which submenu the user chose, check the "__script__"
dictionary in your code: __script__['arg'] has the defined keyword (the word
after the submenu string name: all, sel or gui in the example above) of the
chosen submenu. For example, if the user clicked on submenu 'Selected' above,
__script__['arg'] will be "sel".
If your script requires extra data or configuration files, there is a special
folder where they can be saved: see 'datadir' in L{Blender.Get}.
Interaction with users:
-----------------------
@ -171,53 +127,22 @@ Command line mode:
run scripts from the program itself: you can't import the Blender module
into an external Python interpreter.
But with "OnLoad" script links, the "-b" background mode and additions like
the "-P" command line switch, L{Blender.Save}, L{Blender.Load},
L{Blender.Quit} and the L{Library} module, for many tasks it's possible to
control Blender via some automated process using scripts. Note that command
line scripts are run before Blender initializes its windows (and in '-b' mode
no window will be initialized), so many functions that get or set window
related attributes (like most in L{Window}) don't work here. If you need
those, use an ONLOAD script link (see L{Scene.Scene.addScriptLink}) instead --
it's also possible to use a command line script to write or set an ONLOAD
script link. Check the L{Blender.mode} module var to know if Blender is being
executed in "background" or "interactive" mode.
On the other hand, for many tasks it's possible to control Blender via
some automated process using scripts. Interested readers should learn about
features like "OnLoad" script links, the "-b <blendfile>" (background mode)
and "-P <script>" (run script) command line options and API calls like
L{Blender.Save}, L{Blender.Load}, L{Blender.Quit} and the L{Library} and
L{Render} modules.
Background mode examples::
Note that command line scripts are run before Blender initializes its windows
(and in '-b' mode no window will be initialized), so many functions that get
or set window related attributes (like most in L{Window}) don't work here. If
you need those, use an ONLOAD script link (see L{Scene.Scene.addScriptLink})
instead -- it's also possible to use a command line script to write or set an
ONLOAD script link. Check the L{Blender.mode} module var to know if Blender
is being executed in "background" or "interactive" mode.
# Open Blender in background mode with file 'myfile.blend'
# and run the script 'script.py':
blender -b myfile.blend -P script.py
# Note: a .blend file is always required. 'script.py' can be a file
# in the file system or a Blender Text stored in 'myfile.blend'.
# Let's assume 'script.py' has code to render the current frame;
# this line will set the [s]tart and [e]nd (and so the current) frame to
# frame 44 and call the script:
blender -b myfile.blend -s 44 -e 44 -P script.py
# Using now a script written to render animations, we set different
# start and end frames and then execute this line:
blender -b myfile.blend -s 1 -e 10 -P script.py
# Note: we can also set frames and define if we want a single image or
# an animation in the script body itself, naturally.
The rendered pictures will be written to the default render folder, that can
also be set via bpython (take a look at L{Render.RenderData}). Their
names will be the equivalent frame number followed by the extension of the
chosen image type: 0001.png, for example. To rename them to something else,
coders can use the C{rename} function in the standard 'os' Python module.
Reminder: if you just need to render, it's not necessary to have a script.
Blender can create stills and animations with its own command line arguments.
Example:
- a single image at frame 44: blender -b myfile.blend -f 44
- an animation from frame 1 to 10: blender -b myfile.blend -s 1 -e 10 -a
L{Click here for command line and background mode examples<API_related>}.
Demo mode:
@ -268,74 +193,6 @@ Blender Data Structures:
Blender works the way it does, see the U{Blender Architecture document
<http://www.blender3d.org/cms/Blender_Architecture.336.0.html>}.
Documenting scripts:
--------------------
The "Scripts Help Browser" script in the Help menu can parse special variables
from registered scripts and display help information for users. For that,
authors only need to add proper information to their scripts, after the
registration header.
The expected variables:
- __bpydoc__ (or __doc__) (type: string):
- The main help text. Write a first short paragraph explaining what the
script does, then add the rest of the help text, leaving a blank line
between each new paragraph. To force line breaks you can use <br> tags.
- __author__ (type: string or list of strings):
- Author name(s).
- __version__ (type: string):
- Script version.
- __url__ (type: string or list of strings):
- Internet links that are shown as buttons in the help screen. Clicking
them opens the user's default browser at the specified location. The
expected format for each url entry is e.g.
"Author's site, http://www.somewhere.com". The first part, before the
comma (','), is used as the button's tooltip. There are two preset
options: "blender" and "elysiun", which link to the Python forums at
blender.org and elysiun.com, respectively.
- __email__ (optional, type: string or list of strings):
- Equivalent to __url__, but opens the user's default email client. You
can write the email as someone:somewhere*com and the help script will
substitute accordingly: someone@somewhere.com. This is only a minor help
to hide emails from spammers, since your script may be available at some
site. "scripts" is the available preset, with the email address of the
mailing list devoted to scripting in Blender, bf-scripts-dev@blender.org.
You should only use this one if you are subscribed to the list:
http://projects.blender.org/mailman/listinfo/bf-scripts-dev for more
information.
Example::
__author__ = 'Mr. Author'
__version__ = '1.0 2005/06/06'
__url__ = ["Author's site, http://somewhere.com",
"Support forum, http://somewhere.com/forum/", "blender", "elysiun"]
__email__ = ["Mr. Author, mrauthor:somewhere*com", "scripts"]
__bpydoc__ = \"\"\"\\
This script does this and that.
Explaining better, this script helps you create ...
You can write as many paragraphs as needed.
Shortcuts:<br>
Esc or Q: quit.<br>
etc.
Supported:<br>
Meshes, metaballs.
Known issues:<br>
This is just an example, there's no actual script.
Notes:<br>
You can check scripts bundled with Blender to see more examples of how to
add documentation to your own works.
\"\"\"
A note to newbie script writers:
--------------------------------
@ -348,8 +205,8 @@ A note to newbie script writers:
to get an idea of what can be done, you may be surprised.
@author: The Blender Python Team
@requires: Blender 2.35 or newer.
@version: 2.35 - 2.36
@requires: Blender 2.36 cvs or newer.
@version: 2.36 cvs
@see: U{www.blender3d.org<http://www.blender3d.org>}: main site
@see: U{www.blender.org<http://www.blender.org>}: documentation and forum
@see: U{www.elysiun.com<http://www.elysiun.com>}: user forum

@ -0,0 +1,377 @@
# This is not a real module, it's simply an introductory text.
"""
Blender Python related features
===============================
L{Back to Main Page<API_intro>}
Introduction:
=============
This page describes special features available to BPython scripts:
- Command line mode is accessible with the '-P' and '-b' Blender options.
- Registration allows scripts to become available from some pre-defined menus
in Blender, like Import, Export, Wizards and so on.
- Proper documentation data is used by the 'Scripts Help Browser' script to
show help information for any registered script. Your own GUI can use
this facility with the L{Blender.ShowHelp} function.
- Configuration is for data in your script that can be tweaked according to
user taste or needs. Like documentation, this is another helper
functionality -- you don't need to provide a GUI yourself to edit config
data.
Command line usage:
-------------------
B{Specifying scripts}:
The '-P' option followed either by:
- a script filename (full pathname if not in the same folder where you run
the command);
- the name of a Text in a .blend file (that must also be specified)
will open Blender and immediately run the given script.
Example::
# open Blender and execute the given script:
blender -P script.py
B{Passing parameters}:
To pass parameters to the script you can:
- write them to a file before running Blender, then make your script parse that file;
- set environment variables and access them with the 'os' module:
Examples with parameters being passed to the script via command line::
# execute a command like:
myvar=value blender -P script.py
# and in script.py access myvar with os.getenv
# (os.environ and os.setenv are also useful):
# script.py:
import os
val = os.getenv('myvar')
# To pass multiple parameters, simply write them in sequence,
# separated by spaces:
myvar1=value1 myvar2=value2 mystr="some string data" blender -P script.py
B{Background mode}:
In '-b' mode no windows will be opened: the program will run as a command
line tool able to render stills and animations and execute any working Python
script with complete access to loaded .blend's file contents. Once the task
is completed, the program will exit.
Background mode examples::
# Open Blender in background mode with file 'myfile.blend'
# and run the script 'script.py':
blender -b myfile.blend -P script.py
# Note: a .blend file is always required. 'script.py' can be a file
# in the file system or a Blender Text stored in 'myfile.blend'.
# Let's assume 'script.py' has code to render the current frame;
# this line will set the [s]tart and [e]nd (and so the current) frame to
# frame 44 and call the script:
blender -b myfile.blend -s 44 -e 44 -P script.py
# Using now a script written to render animations, we set different
# start and end frames and then execute this line:
blender -b myfile.blend -s 1 -e 10 -P script.py
# Note: we can also set frames and define if we want a single image or
# an animation in the script body itself, naturally.
The rendered pictures will be written to the default render folder, that can
also be set via bpython (take a look at L{Render.RenderData}). Their
names will be the equivalent frame number followed by the extension of the
chosen image type: 0001.png, for example. To rename them to something else,
coders can use the C{rename} function in the standard 'os' Python module.
Reminder: if you just need to render, it's not necessary to have a script.
Blender can create stills and animations with its own command line arguments.
Example:
- a single image at frame 44: blender -b myfile.blend -f 44
- an animation from frame 1 to 10: blender -b myfile.blend -s 1 -e 10 -a
Registering scripts:
--------------------
To be registered a script needs two things:
- to be either in the default scripts dir or in the user defined scripts
path (see User Preferences window -> File Paths tab -> Python path);
- to have a proper header.
Try 'blender -d' to know where your default dir for scripts is, it will
inform either the dir or the file with that info already parsed, which is
in the same dir of the scripts folder.
The header should be like this one (all double and single apostrophes below
are required)::
#!BPY
# \"\"\"
# Name: 'Script Name'
# Blender: 233
# Group: 'Export'
# Submenu: 'All' all
# Submenu: 'Selected' sel
# Submenu: 'Configure (gui)' gui
# Tooltip: 'Export to some format.'
# \"\"\"
where:
- B{Name} is the string that will appear in the menu;
- B{Blender} is the minimum program version required to run the script;
- B{Group} defines where the script will be put, see all groups in the
Scripts Window's header, menu "Scripts";
- B{Submenu} adds optional submenus for further control;
- B{Tooltip} is the (short) tooltip string for the menu entry.
note:
- all double and single apostrophes above are required;
- you can "comment out" the header above, by starting lines with
'#', like we did. This is not required (except for the first line, #!BPY,
of course), but this way the header won't conflict with Python tools that
you can use to generate documentation for your script code. Just
remember to keep this header above any other line with triple
double-quotes (\"\"\") in your script.
Submenu lines are not required, use them if you want to provide extra
options. To see which submenu the user chose, check the "__script__"
dictionary in your code: __script__['arg'] has the defined keyword (the word
after the submenu string name: all, sel or gui in the example above) of the
chosen submenu. For example, if the user clicked on submenu 'Selected' above,
__script__['arg'] will be "sel".
If your script requires extra data or configuration files, there is a special
folder where they can be saved: see 'datadir' in L{Blender.Get}.
Documenting scripts:
--------------------
The "Scripts Help Browser" script in the Help menu can parse special variables
from registered scripts and display help information for users. For that,
authors only need to add proper information to their scripts, after the
registration header.
The expected variables:
- __bpydoc__ (or __doc__) (type: string):
- The main help text. Write a first short paragraph explaining what the
script does, then add the rest of the help text, leaving a blank line
between each new paragraph. To force line breaks you can use <br> tags.
- __author__ (type: string or list of strings):
- Author name(s).
- __version__ (type: string):
- Script version. A good recommendation is using a version number followed
by the date in the format YYYY/MM/DD: "1.0 2005/12/31".
- __url__ (type: string or list of strings):
- Internet links that are shown as buttons in the help screen. Clicking
them opens the user's default browser at the specified location. The
expected format for each url entry is e.g.
"Author's site, http://www.somewhere.com". The first part, before the
comma (','), is used as the button's tooltip. There are two preset
options: "blender" and "elysiun", which link to the Python forums at
blender.org and elysiun.com, respectively.
- __email__ (optional, type: string or list of strings):
- Equivalent to __url__, but opens the user's default email client. You
can write the email as someone:somewhere*com and the help script will
substitute accordingly: someone@somewhere.com. This is only a minor help
to hide emails from spammers, since your script may be available at some
site. "scripts" is the available preset, with the email address of the
mailing list devoted to scripting in Blender, bf-scripts-dev@blender.org.
You should only use this one if you are subscribed to the list:
http://projects.blender.org/mailman/listinfo/bf-scripts-dev for more
information.
Example::
__author__ = 'Mr. Author'
__version__ = '1.0 2005/01/01'
__url__ = ["Author's site, http://somewhere.com",
"Support forum, http://somewhere.com/forum/", "blender", "elysiun"]
__email__ = ["Mr. Author, mrauthor:somewhere*com", "scripts"]
__bpydoc__ = \"\"\"\\
This script does this and that.
Explaining better, this script helps you create ...
You can write as many paragraphs as needed.
Shortcuts:<br>
Esc or Q: quit.<br>
etc.
Supported:<br>
Meshes, metaballs.
Known issues:<br>
This is just an example, there's no actual script.
Notes:<br>
You can check scripts bundled with Blender to see more examples of how to
add documentation to your own works.
\"\"\"
B{Note}: your own GUI or menu code can display documentation by calling the
help browser with the L{Blender.ShowHelp} function.
Configuring scripts:
--------------------
Configuration data is simple data used by your script (bools, ints, floats,
strings) to define default behaviors.
For example, an exporter might have:
- EXPORT_LIGHTS = False: a bool variable (True / False) to determine if it
should also export lights setup information;
- VERSION = 2.0: an int to define an specific version of the export format;
- TEX_DIR = "/path/to/textures": a default texture dir to prepend to all
exported texture filenames instead of their actual paths.
To properly handle this, script writers had to keep this information in a
separate config file (at L{Blender.Get}('udatadir') or, if not available,
L{Blender.Get}('datadir')), provide a GUI to edit it and update the file
whenever needed.
There are facilities in BPython now to take care of this in a simplified (and
much recommended) way.
The L{Registry} module functions L{GetKey<Registry.GetKey>} and
L{SetKey<Registry.SetKey>} take care of both keeping the data in Blender
and (new) storing it in config files at the proper dir. And the 'Scripts
Configuration Editor' script provides a GUI for users to view and edit
configuration data.
Here's how it works::
# sample_exporter.py
import Blender
from Blender import Registry
# First define all config variables with their default values:
EXPORT_LIGHTS = True
VERBOSE = True
EXPORT_DIR = ''
# Then define a function to update the Registry:
def registry_update():
# populate a dict with current config values:
d = {
'EXPORT_LIGHTS': EXPORT_LIGHTS,
'VERBOSE': VERBOSE,
'EXPORT_DIR': EXPORT_DIR
}
# store the key (optional 3rd arg tells if
# the data should also be written to a file):
Registry.SetKey('sample_exporter', d, True)
# (A good convention is to use the script name as Registry key)
# Now we check if our key is available in the Registry or file system:
regdict = Registry.GetKey('sample_exporter', True)
# If this key already exists, update config variables with its values:
if regdict:
EXPORT_LIGHTS = regdict['EXPORT_LIGHTS']
VERBOSE = regdict['VERBOSE']
EXPORT_DIR = regdict['EXPORT_DIR']
else: # if the key doesn't exist yet, use our function to create it:
update_registry()
# ...
Hint: nicer code than the simplistic example above can be written by keeping
config var names in a list of strings and using the exec function.
B{Note}: if you have a gui and the user uses it to change config vars,
call the registry_update() function to save the changes.
On the other hand, you don't need to handle configuration
in your own gui, it can be left for the 'Scripts Config Editor',
which should have access to your script's config key as soon as the
above code is executed once.
As written above, config vars can be bools, ints, floats or strings. This is
what the Config Editor supports, with sensible but generous limits for the
number of vars and the size of each string. Restrictions were suggested or
imposed to these facilities related to the Registry module because it's meant
for configuration info, not for large volumes of data. For that you can
trivially store it in a file or Blender Text yourself -- and tell the user
about it, specially if your script keeps megabytes of data in the Registry
memory.
B{Scripts Configuration Editor}:
This script should be available from the Help menu and provides a GUI to
view and edit saved configuration data, both from the Registry dictionary in
memory and the scripts config data dir.
The example above already gives a good idea of how the information can be
prepared to be accessible from this editor, but there is more worth knowing:
1. String vars that end with '_dir' or '_file' (can be upper case, too) are
recognized as input boxes for dirs or files and a 'browse' button is added to
their right side, to call the file selector.
2. Both key names and configuration variables names starting with an
underscore ('_') are ignored by the editor. Programmers can use this feature
for any key or config var that is not meant to be configured by this editor.
3. The following information refers to extra config variables that may be
added specifically to aid the configuration editor script. To clarify, in the
example code above these variables (the string 'script' and the dictionaries
'tooltips' and 'limits') would appear along with EXPORT_LIGHTS, VERBOSE and
EXPORT_DIR, wherever they are written.
Minor note: these names are case insensitive: tooltips, TOOLTIPS, etc. are all
recognized.
3.1 The config editor will try to display a 'help' button for a key, to show
documentation for the script that owns it. To find this "owner script", it
will first look for a config variable called 'script', a string containing
the name of the owner Python file (with or without '.py' extension)::
script = 'sample_exporter.py'
If there is no such variable, the editor will check if the file formed by the
key name and the '.py' extension exists. If both alternatives fail, no help
button will be displayed.
3.2 You can define tooltips for the buttons that the editor creates for your
config data (string input, toggle, number sliders). Simply create a dict
called 'tooltips', where config var names are keys and their tooltips,
values::
tooltips = {
'EXPORT_DIR': 'default folder where exported files should be saved',
'VERBOSE': 'print info and warning messages to the console',
'EXPORT_LIGHTS': 'export scene lighting setup'
}
3.3 Int and float button sliders need min and max limits. This can be passed
to the editor via a dict called 'limits' (ivar1, ivar2 and fvar are meant as
extra config vars that might have been in the example code above)::
limits = {'ivar1': [-10, 10], 'ivar2': [0, 100], 'fvar1': [-12.3, 15.4]}
L{Back to Main Page<API_intro>}
"""

@ -10,7 +10,7 @@
"""
The main Blender module.
B{New}: L{Run}, L{UpdateMenus}, new options to L{Get}.
B{New}: L{Run}, L{UpdateMenus}, new options to L{Get}, L{ShowHelp}.
Blender
=======
@ -61,13 +61,23 @@ def Get (request):
available (is None if not found), but users that define uscriptsdir
have a place for their own scripts and script data that won't be
erased when a new version of Blender is installed.
- 'scriptsdir': the path to the main dir where scripts are stored
(can be None, if not found).
- 'uscriptsdir': the path to the user defined dir for scripts, see
the paths tab in the User Preferences window in Blender
(can be None, if not found).
- 'scriptsdir': the path to the main dir where scripts are stored.
- 'uscriptsdir': the path to the user defined dir for scripts. (*)
- 'yfexportdir': the path to the user defined dir for yafray export. (*)
- 'fontsdir': the path to the user defined dir for fonts. (*)
- 'texturesdir': the path to the user defined dir for textures. (*)
- 'texpluginsdir': the path to the user defined dir for texture plugins. (*)
- 'seqpluginsdir': the path to the user defined dir for sequence plugins. (*)
- 'renderdir': the path to the user defined dir for render output. (*)
- 'soundsdir': the path to the user defined dir for sound files. (*)
- 'tempdir': the path to the user defined dir for storage of Blender
temporary files. (*)
- 'version' : the Blender version number.
@return: The requested data.
@note: (*) these can be set in Blender at the User Preferences window -> File
Paths tab.
@warn: this function returns None for requested dir paths that have not been
set or do not exist in the user's file system.
@return: The requested data or None if not found.
"""
def Redraw ():
@ -122,8 +132,8 @@ def Save (filename, overwrite = 0):
of the supported extensions or an error will be returned.
@type overwrite: int (bool)
@param overwrite: if non-zero, file 'filename' will be overwritten if it
already exists. By default existing files are not overwritten (an error
is returned).
already exists (can be checked with L{Blender.sys.exists<Sys.exists>}.
By default existing files are not overwritten (an error is returned).
@note: The substring ".B.blend" is not accepted inside 'filename'.
@note: DXF, STL and Videoscape export only B{selected} meshes.
@ -136,8 +146,28 @@ def Run (script):
@param script: the name of an available Blender Text (use L{Text.Get}() to
get a complete list) or the full pathname to a Python script file in the
system.
@note: the script is executed in its own context (with its own global
dictionary), as if you had called it with ALT+P or chosen from a menu.
@note: the script is executed in its own context -- with its own global
dictionary -- as if it had been executed from the Text Editor or chosen
from a menu.
"""
def ShowHelp (script):
"""
Show help for the given script. This is a time-saver ("code-saver") for
scripts that need to feature a 'help' button in their GUI's or a 'help'
submenu option. With proper documentation strings, calling this function is
enough to present a screen with help information plus link and email buttons.
@type script: string
@param script: the filename of a registered Python script.
@note: this function uses L{Run} and the "Scripts Help Browser" script. This
means that it expects proper doc strings in the script to be able to show
help for it (otherwise it offers to load the script source code as text).
The necessary information about doc strings is in the
L{Intro page<API_intro>} of this API Reference documentation you're
reading.
@note: 'script' doesn't need to be a full path name: "filename.py" is enough.
Note, though, that this function only works for properly registered
scripts (those that appear in menus).
"""
def UpdateMenus ():

@ -192,7 +192,23 @@ All available events:
- ZEROKEY
- ZKEY
@note: function Button has a new alias: L{PushButton}.
@note: function Button has an alias: L{PushButton}.
@warn: B{very important}: if using your script causes "Error totblock"
messages when Blender exits (meaning that memory has been leaked), this may
have been caused by an ignored return value from one of the button types. To
avoid this, assign created buttons return values to B{global} variables,
instead of ignoring them. Examples::
# avoid this, it can cause memory leaks:
Draw.Toggle(...)
Draw.Number(...)
Draw.String(...)
# this is correct -- assuming the variables are globals:
my_toggle_button = Draw.Toggle(...)
my_int_button = Draw.Number(...)
my_str_button = Draw.String(...)
@warn: Inside the windowing loop (after Draw.Register() has been executed and
before Draw.Exit() is called), don't use the redraw functions from other
@ -484,36 +500,36 @@ def Slider(name, event, x, y, width, height, initial, min, max, realtime = 1,
@return: The Button created.
"""
def Scrollbar(event, x, y, width, height, initial, min, max, realtime = 1,
tooltip = None):
"""
Create a new Scrollbar Button object.
@type event: int
@param event: The event number to pass to the button event function when
activated.
@type x: int
@type y: int
@param x: The lower left x (horizontal) coordinate of the button.
@param y: The lower left y (vertical) coordinate of the button.
@type width: int
@type height: int
@param width: The button width.
@param height: The button height.
@type initial: int or float
@type min: int or float
@type max: int or float
@param initial: The initial value.
@param min: The minimum value.
@param max: The maximum value.
@type realtime: int
@param realtime: If non-zero (the default), the slider will emit events as
it is edited.
@type tooltip: string
@param tooltip: The button's tooltip (the string that appears when the mouse
is kept over the button).
@rtype: Blender Button
@return: The Button created.
"""
#def Scrollbar(event, x, y, width, height, initial, min, max, realtime = 1,
# tooltip = None):
# """
# Create a new Scrollbar Button object.
# @type event: int
# @param event: The event number to pass to the button event function when
# activated.
# @type x: int
# @type y: int
# @param x: The lower left x (horizontal) coordinate of the button.
# @param y: The lower left y (vertical) coordinate of the button.
# @type width: int
# @type height: int
# @param width: The button width.
# @param height: The button height.
# @type initial: int or float
# @type min: int or float
# @type max: int or float
# @param initial: The initial value.
# @param min: The minimum value.
# @param max: The maximum value.
# @type realtime: int
# @param realtime: If non-zero (the default), the slider will emit events as
# it is edited.
# @type tooltip: string
# @param tooltip: The button's tooltip (the string that appears when the mouse
# is kept over the button).
# @rtype: Blender Button
# @return: The Button created.
# """
def Number(name, event, x, y, width, height, initial, min, max, tooltip = None):
"""

@ -804,11 +804,11 @@ class Property:
"""
Set the the Object's Particle Interaction type.
Use Module Constants
NONE
WIND
FORCE
VORTEX
MAGNET
- NONE
- WIND
- FORCE
- VORTEX
- MAGNET
@rtype: PyNone
@type type: int
@param type: the Object's Particle Interaction Type.
@ -902,7 +902,7 @@ class Property:
Values between 0 to 50.0
@rtype: PyNone
@type mass: float
@param damp: the Object's SB New mass.
@param mass: the Object's SB New mass.
"""
def getSBGravity():
@ -917,7 +917,7 @@ class Property:
Values between 0 to 10.0
@rtype: PyNone
@type grav: float
@param damp: the Object's SB New Gravity.
@param grav: the Object's SB New Gravity.
"""
def getSBFriction():
@ -932,7 +932,7 @@ class Property:
Values between 0 to 10.0
@rtype: PyNone
@type frict: float
@param damp: the Object's SB New Friction.
@param frict: the Object's SB New Friction.
"""
def getSBErrorLimit():
@ -947,7 +947,7 @@ class Property:
Values between 0 to 1.0
@rtype: PyNone
@type err: float
@param damp: the Object's SB New ErrorLimit.
@param err: the Object's SB New ErrorLimit.
"""
def getSBGoalSpring():
@ -962,7 +962,7 @@ class Property:
Values between 0 to 0.999
@rtype: PyNone
@type gs: float
@param damp: the Object's SB New GoalSpring.
@param gs: the Object's SB New GoalSpring.
"""
def getSBGoalFriction():
@ -977,7 +977,7 @@ class Property:
Values between 0 to 10.0
@rtype: PyNone
@type gf: float
@param damp: the Object's SB New GoalFriction.
@param gf: the Object's SB New GoalFriction.
"""
def getSBMinGoal():
@ -992,7 +992,7 @@ class Property:
Values between 0 to 1.0
@rtype: PyNone
@type mg: float
@param damp: the Object's SB New MinGoal.
@param mg: the Object's SB New MinGoal.
"""
def getSBMaxGoal():
@ -1007,7 +1007,7 @@ class Property:
Values between 0 to 1.0
@rtype: PyNone
@type mg: float
@param damp: the Object's SB New MaxGoal.
@param mg: the Object's SB New MaxGoal.
"""
def getSBInnerSpring():
@ -1021,8 +1021,8 @@ class Property:
Set the the Object's SB InnerSpring.
Values between 0 to 0.999
@rtype: PyNone
@type spr: float
@param damp: the Object's SB New InnerSpring.
@type sprr: float
@param sprr: the Object's SB New InnerSpring.
"""
def getSBInnerSpringFriction():
@ -1037,7 +1037,7 @@ class Property:
Values between 0 to 10.0
@rtype: PyNone
@type sprf: float
@param damp: the Object's SB New InnerSpringFriction.
@param sprf: the Object's SB New InnerSpringFriction.
"""
def getSBDefaultGoal():
@ -1052,7 +1052,7 @@ class Property:
Values between 0 to 1.0
@rtype: PyNone
@type goal: float
@param damp: the Object's SB New DefaultGoal.
@param goal: the Object's SB New DefaultGoal.
"""
def getSBEnable():
@ -1068,7 +1068,7 @@ class Property:
0: off
@rtype: PyNone
@type switch: int
@param damp: the Object's SB New Enable Value.
@param switch: the Object's SB New Enable Value.
"""
def getSBPostDef():
@ -1084,7 +1084,7 @@ class Property:
0: off
@rtype: PyNone
@type switch: int
@param damp: the Object's SB New PostDef Value.
@param switch: the Object's SB New PostDef Value.
"""
def getSBUseGoal():
@ -1100,7 +1100,7 @@ class Property:
0: off
@rtype: PyNone
@type switch: int
@param damp: the Object's SB New UseGoal Value.
@param switch: the Object's SB New UseGoal Value.
"""
def getSBUseEdges():
"""
@ -1115,7 +1115,7 @@ class Property:
0: off
@rtype: PyNone
@type switch: int
@param damp: the Object's SB New UseEdges Value.
@param switch: the Object's SB New UseEdges Value.
"""
def getSBStiffQuads():
@ -1131,5 +1131,5 @@ class Property:
0: off
@rtype: PyNone
@type switch: int
@param damp: the Object's SB New StiffQuads Value.
@param switch: the Object's SB New StiffQuads Value.
"""

@ -3,6 +3,8 @@
"""
The Blender.Registry submodule.
B{New}: L{GetKey} and L{SetKey} can respectively load and save data to disk now.
Registry
========
@ -17,19 +19,19 @@ give script authors a way around this limitation.
In Python terms, the Registry holds a dictionary of dictionaries.
You should use it to save Python objects only, not BPython (Blender Python)
objects -- but you can save BPython object names, since those are strings.
Also, if you need to save a considerable amount of data, please save to a
file instead. There's no need to keep huge blocks of memory around when they
can simply be read from a file.
Also, if you need to save a considerable amount of data, we recommend saving
it to a file instead. There's no need to keep huge blocks of memory around when
they can simply be read from a file.
Two uses for this module:
Examples of what this module can be used for:
a) To save data from a script that another script will need to access later.
a) Saving data from a script that another script will need to access later.
b) To save configuration data from your script's gui (button values) so that the
next time the user runs your script, the changes will still be there. Later we
can make Blender save the Registry so that its data won't be lost after users
quit the program. And also add an option to save as a Text that can be kept in
a .blend file, letting users keep script data there.
b) Saving configuration data for a script. Users can view and edit this data
using the "Scripts Configuration Editor" script, then.
c) Saving configuration data from your script's gui (button values) so that the
next time the user runs your script, the changes will still be there.
Example::
@ -42,12 +44,12 @@ Example::
mystr = "hello"
# then check if they are already at the Registry (saved on a
# previous execution of this script):
dict = Registry.GetKey('MyScript')
if dict: # if found, get the values saved there
myvar1 = dict['myvar1']
myvar2 = dict['myvar2']
mystr = dict['mystr']
# previous execution of this script) or on disk:
rdict = Registry.GetKey('MyScript', True)
if rdict: # if found, get the values saved there
myvar1 = rdict['myvar1']
myvar2 = rdict['myvar2']
mystr = rdict['mystr']
# let's create a function to update the Registry when we need to:
def update_Registry():
@ -55,7 +57,8 @@ Example::
d['myvar1'] = myvar1
d['myvar2'] = myvar2
d['mystr'] = mystr
Blender.Registry.SetKey('MyScript', d)
# cache = True: data is also saved to a file
Blender.Registry.SetKey('MyScript', d, True)
# ...
# here goes the main part of the script ...
@ -72,21 +75,29 @@ def Keys ():
Get all keys currently in the Registry's dictionary.
"""
def GetKey (key):
def GetKey (key, cached = False):
"""
Get key 'key' from the Registry.
@type key: string
@param key: a key from the Registry dictionary.
@type cached: bool
@param cached: if True and the requested key isn't already loaded in the
Registry, it will also be searched on the user or default scripts config
data dir (config subdir in L{Blender.Get}('datadir')).
@return: the dictionary called 'key'.
"""
def SetKey (key, dict):
def SetKey (key, dict, cache = False):
"""
Store a new entry in the Registry.
@type key: string
@param key: the name of the new entry, tipically your script's name.
@type dict: dictionary
@param dict: a dict with all data you want to save in the Registry.
@type cache: bool
@param cache: if True the given key data will also be saved as a file
in the config subdir of the scripts user or default data dir (see
L{Blender.Get}.
"""
def RemoveKey (key):

@ -3,8 +3,8 @@
"""
The Blender.Scene.Render submodule.
Scene
=====
Scene.Render
============
This module provides access to B{Scene Rendering Contexts} in Blender.

@ -6,7 +6,7 @@ The Blender.sys submodule.
sys
===
B{New}: L{exists}, L{makename}, L{join}, L{sleep}.
B{New}: L{expandpath}.
This module provides a minimal set of helper functions and data. Its purpose
is to avoid the need for the standard Python module 'os', in special 'os.path',
@ -137,3 +137,22 @@ def sleep (millisecs = 10):
@param millisecs: the amount of time in milliseconds to sleep. The default
is 10 which is 0.1 seconds.
"""
def expandpath (path):
"""
Expand the given Blender 'path' into an absolute and valid path.
Internally, Blender recognizes two special character sequences in paths:
- '//' (used at the beginning): means base path -- the current .blend file's
dir;
- '#' (used at the end): means current frame number.
The expanded string can be passed to generic python functions that don't
understand Blender's internal relative paths.
@note: this function is also useful for obtaining the name of the image
that will be saved when rendered.
@note: if the passed string doesn't contain the special characters it is
returned unchanged.
@type path: string
@param path: a path name.
@rtype: string
@return: the expanded (if necessary) path.
"""