1
0
Fork 0

UV<->SVG exporter and importer: The importer loads an SVG file that was

saved before by the exporter. SVG editors like Inkscape can be used to
move the UV outlines around, to rotate and scale them.
This commit is contained in:
mfranz 2008-03-05 12:30:01 +00:00
parent b8de58bcdd
commit 2fcae160a1
2 changed files with 423 additions and 0 deletions

View file

@ -0,0 +1,130 @@
#!BPY
# """
# Name: 'SVG: Export UV layout to SVG file'
# Blender: 245
# Group: 'UV'
# Tooltip: 'Export selected objects to SVG file'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = "http://members.aon.at/mfranz/flightgear/"
__version__ = "0.1"
__bpydoc__ = """\
Saves the UV mappings of all selected files to an SVG file. The uv_import_svg.py
script can be used to re-import such a file. Each object and each group of adjacent
faces therein will be put into an SVG group.
"""
ID_SEPARATOR = '#'
import Blender, sys
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
def get_adjacent(pool):
i, face = pool.popitem()
group = [face]
uvcoords = {}
for c in face.uv:
uvcoords[(c[0], c[1])] = True
while True:
found = []
for face in pool.itervalues():
for c in face.uv:
if (c[0], c[1]) in uvcoords:
for d in face.uv:
uvcoords[(d[0], d[1])] = True
found.append(face)
break
if not found:
break
for face in found:
group.append(face)
del pool[face.index]
return group
def write_svg(filename):
size = Blender.Draw.PupMenu("Image size%t|128|256|512|1024|2048|4096|8192")
if size < 0:
raise Abort('no image size chosen')
size = 1 << (size + 6)
print "exporting to '%s' (size %d) ... " % (filename, size),
svg = open(filename, "w")
svg.write('<?xml version="1.0" standalone="no"?>\n')
svg.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n\n')
svg.write('<svg width="%spx" height="%spx" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg"' \
'version="1.1" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">\n'
% (size, size, size, size))
svg.write("\t<desc>uv_export_svg.py: %s</desc>\n" % filename);
svg.write('\t<rect x="0" y="0" width="%d" height="%d" fill="none" stroke="blue" stroke-width="%f"/>\n'
% (size, size, 1.0))
unique_meshes = {}
for o in Blender.Scene.GetCurrent().objects.selected:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if not mesh.faceUV:
continue
if mesh.name in unique_meshes:
#print "dropping duplicate mesh", mesh.name, "of object", o.name
continue
unique_meshes[mesh.name] = True
svg.write('\t<g style="fill:yellow; stroke:black stroke-width:1px" inkscape:label="%s" id="%s">\n' % (o.name, o.name))
pool = {}
for f in mesh.faces:
pool[f.index] = f
while len(pool):
svg.write('\t\t<g>\n')
for f in get_adjacent(pool):
svg.write('\t\t\t<polygon points="')
for p in f.uv:
svg.write('%.8f,%.8f ' % (p[0] * size, size - p[1] * size))
svg.write('" id="%s%s%d"/>\n' % (mesh.name, ID_SEPARATOR, f.index))
svg.write('\t\t</g>\n')
svg.write("\t</g>\n")
svg.write('</svg>\n')
svg.close()
print "done."
def export(filename):
registry = {}
registry[basename] = Blender.sys.basename(filename)
Blender.Registry.SetKey("UVImportExportSVG", registry, False)
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
try:
write_svg(filename)
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
if editmode:
Blender.Window.EditMode(1)
active = Blender.Scene.GetCurrent().objects.active
(basename, extname) = Blender.sys.splitext(Blender.Get("filename"))
filename = Blender.sys.basename(basename) + "-" + active.name + ".svg"
Blender.Window.FileSelector(export, "Export to SVG", filename)

293
utils/Modeller/uv_import_svg.py Executable file
View file

@ -0,0 +1,293 @@
#!BPY
# """
# Name: 'SVG: Re-Import UV layout from SVG file'
# Blender: 245
# Group: 'UV'
# Tooltip: 'Re-import UV layout from SVG file'
# """
__author__ = "Melchior FRANZ < mfranz # aon : at >"
__url__ = "http://members.aon.at/mfranz/flightgear/"
__version__ = "0.1"
__bpydoc__ = """\
Imports an SVG file containing UV maps, which has been saved by the
uv_export.svg script. This allows to move, scale, and rotate object
mapping in SVG editors like Inkscape. Note that all contained UV maps
will be set, no matter which objects are actually selected at the moment.
The choice has been made when the file was saved!
"""
ID_SEPARATOR = '#'
import Blender, sys, math, re
from xml.sax import saxexts
registry = {}
numwsp = re.compile('(?<=[\d.])\s+(?=[-+.\d])')
commawsp = re.compile('\s+|\s*,\s*')
istrans = re.compile('^\s*(skewX|skewY|scale|translate|rotate|matrix)\s*\(([^\)]*)\)\s*')
isnumber = re.compile('^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$')
class Abort(Exception):
def __init__(self, msg):
self.msg = msg
class Matrix:
def __init__(self, a = 1, b = 0, c = 0, d = 1, e = 0, f = 0):
self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
def __str__(self):
return "[Matrix %f %f %f %f %f %f]" % (self.a, self.b, self.c, self.d, self.e, self.f)
def multiply(self, mat):
a = self.a * mat.a + self.c * mat.b
b = self.b * mat.a + self.d * mat.b
c = self.a * mat.c + self.c * mat.d
d = self.b * mat.c + self.d * mat.d
e = self.a * mat.e + self.c * mat.f + self.e
f = self.b * mat.e + self.d * mat.f + self.f
self.a = a; self.b = b; self.c = c; self.d = d; self.e = e; self.f = f
def transform(self, u, v):
x = u * self.a + v * self.c + self.e
y = u * self.b + v * self.d + self.f
return (x, y)
def translate(self, dx, dy):
self.multiply(Matrix(1, 0, 0, 1, dx, dy))
def scale(self, sx, sy):
self.multiply(Matrix(sx, 0, 0, sy, 0, 0))
def rotate(self, a):
a *= math.pi / 180
self.multiply(Matrix(math.cos(a), math.sin(a), -math.sin(a), math.cos(a), 0, 0))
def skewX(self, a):
a *= math.pi / 180
self.multiply(Matrix(1, 0, math.tan(a), 1, 0, 0))
def skewY(self, a):
a *= math.pi / 180
self.multiply(Matrix(1, math.tan(a), 0, 1, 0, 0))
def parse_transform(s):
matrix = Matrix()
while True:
match = istrans.match(s)
if not match:
break
cmd = match.group(1)
values = commawsp.split(match.group(2).strip())
s = s[len(match.group(0)):]
arg = []
for value in values:
match = isnumber.match(value)
if not match:
raise Abort("bad transform value")
arg.append(float(match.group(0)))
num = len(arg)
if cmd == "skewX":
if num == 1:
matrix.skewX(arg[0])
continue
elif cmd == "skewY":
if num == 1:
matrix.skewY(arg[0])
continue
elif cmd == "scale":
if num == 1:
matrix.scale(arg[0], arg[0])
continue
if num == 2:
matrix.scale(arg[0], arg[1])
continue
elif cmd == "translate":
if num == 1:
matrix.translate(arg[0], 0)
continue
if num == 2:
matrix.translate(arg[0], arg[1])
continue
elif cmd == "rotate":
if num == 1:
matrix.rotate(arg[0])
continue
if num == 3:
matrix.translate(-arg[1], -arg[2])
matrix.rotate(arg[0])
matrix.translate(arg[1], arg[2])
continue
elif cmd == "matrix":
if num == 6:
matrix.multiply(Matrix(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]))
continue
else:
print "ERROR: unknown transform", cmd
continue
print "ERROR: '%s' with wrong argument number (%d)" % (cmd, num)
if len(s):
print "ERROR: transform with trailing garbage (%s)" % s
return matrix
class import_svg:
# err_handler
def error(self, exception):
raise Abort(str(exception))
def fatalError(self, exception):
raise Abort(str(exception))
def warning(self, exception):
print "WARNING: " + str(exception)
# doc_handler
def setDocumentLocator(self, whatever):
pass
def startDocument(self):
self.verified = False
self.scandesc = False
self.matrices = [None]
self.meshes = {}
for o in Blender.Scene.GetCurrent().objects:
if o.type != "Mesh":
continue
mesh = o.getData(mesh = 1)
if not mesh.faceUV:
continue
if mesh.name in self.meshes:
continue
self.meshes[mesh.name] = mesh
def endDocument(self):
pass
def characters(self, data, start, length):
if not self.scandesc:
return
if data[start:start + length].startswith("uv_export_svg.py"):
self.verified = True
def ignorableWhitespace(self, data, start, length):
pass
def startElement(self, name, attrs):
currmat = self.matrices[-1]
if "transform" in attrs:
m = parse_transform(attrs["transform"])
if currmat != None:
m.multiply(currmat)
self.matrices.append(m)
else:
self.matrices.append(currmat)
if name == "polygon":
self.handlePolygon(attrs)
elif name == "svg":
if "viewBox" in attrs:
x, y, w, h = commawsp.split(attrs["viewBox"], 4)
if int(x) or int(y):
raise Abort("bad viewBox")
self.width = int(w)
self.height = int(h)
if self.width != self.height:
raise Abort("viewBox isn't a square")
else:
raise Abort("no viewBox")
elif name == "desc" and not self.verified:
self.scandesc = True
def endElement(self, name):
self.scandesc = False
self.matrices = self.matrices[:-1]
def handlePolygon(self, attrs):
if not self.verified:
raise Abort("this file wasn't written by uv_export_svg.py")
ident = attrs.get("id", None)
points = attrs.get("points", None)
if not ident or not points:
print('bad polygon "%s"' % ident)
return
sep = ident.find(ID_SEPARATOR)
if sep < 0:
print('broken id "%s"' % ident)
return
meshname = str(ident[:sep])
num = int(ident[sep + 1:])
if not meshname in self.meshes:
print('unknown mesh "%s"' % meshname)
return
#print 'mesh %s face %d: ' % (meshname, num)
matrix = self.matrices[-1]
transuv = []
for p in numwsp.split(points.strip()):
u, v = commawsp.split(p.strip(), 2)
u = float(u)
v = float(v)
if matrix:
u, v = matrix.transform(u, v)
transuv.append((u / self.width, 1 - v / self.height))
for i, uv in enumerate(self.meshes[meshname].faces[num].uv):
uv[0] = transuv[i][0]
uv[1] = transuv[i][1]
def run_parser(filename):
editmode = Blender.Window.EditMode()
if editmode:
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
try:
svg = saxexts.ParserFactory().make_parser("xml.sax.drivers.drv_xmlproc")
svg.setDocumentHandler(import_svg())
svg.setErrorHandler(import_svg())
svg.parse(filename)
except Abort, e:
print "Error:", e.msg, " -> aborting ...\n"
Blender.Draw.PupMenu("Error%t|" + e.msg)
Blender.Window.RedrawAll()
Blender.Window.WaitCursor(0)
if editmode:
Blender.Window.EditMode(1)
active = Blender.Scene.GetCurrent().objects.active
(basename, extname) = Blender.sys.splitext(Blender.Get("filename"))
filename = Blender.sys.basename(basename) + "-" + active.name + ".svg"
registry = Blender.Registry.GetKey("UVImportExportSVG", False)
if registry and basename in registry:
filename = registry[basename]
Blender.Window.FileSelector(run_parser, "Import SVG", filename)