From 5e19642c559ebf8aa8b7e236b3b46d4541505d99 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 26 Jan 2006 15:39:38 +0000 Subject: [PATCH] Speedup by a factor of about 20% --- release/scripts/ply_import.py | 444 +++++++++++++++++----------------- 1 file changed, 228 insertions(+), 216 deletions(-) diff --git a/release/scripts/ply_import.py b/release/scripts/ply_import.py index 4a5a3e52df3..9df72ca8484 100644 --- a/release/scripts/ply_import.py +++ b/release/scripts/ply_import.py @@ -7,8 +7,8 @@ Group: 'Import' Tip: 'Import a Stanford PLY file' """ -__author__ = "Bruce Merry" -__version__ = "0.91" +__author__ = 'Bruce Merry' +__version__ = '0.92' __bpydoc__ = """\ This script imports Stanford PLY files into Blender. It supports per-vertex normals, and per-face colours and texture coordinates. @@ -35,6 +35,8 @@ Run this script from "File->Import" and select the desired PLY file. # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# Updated by Campbell Barton AKA Ideasman, 10% faster code. + # Portions of this code are taken from mod_meshtools.py in Blender # 2.32. @@ -42,239 +44,249 @@ import Blender, meshtools import re, struct, StringIO class element_spec: - name = "" - count = 0 - def __init__(self, name, count): - self.name = name - self.count = count - self.properties = [] + name = '' + count = 0 + def __init__(self, name, count): + self.name = name + self.count = count + self.properties = [] - def load(self, format, stream): - if format == "ascii": - stream = re.split('\s+', stream.readline()) - return map(lambda x: x.load(format, stream), self.properties) + def load(self, format, stream): + if format == 'ascii': + stream = re.split('\s+', stream.readline()) + return map(lambda x: x.load(format, stream), self.properties) - def index(self, name): - for p in range(len(self.properties)): - if self.properties[p].name == name: return p - return -1 + def index(self, name): + for i, p in enumerate(self.properties): + if p.name == name: return i + return -1 class property_spec: - name = "" - list_type = "" - numeric_type = "" - def __init__(self, name, list_type, numeric_type): - self.name = name - self.list_type = list_type - self.numeric_type = numeric_type + name = '' + list_type = '' + numeric_type = '' + def __init__(self, name, list_type, numeric_type): + self.name = name + self.list_type = list_type + self.numeric_type = numeric_type - def read_format(self, format, count, num_type, stream): - if format == "ascii": - if (num_type == 's'): - ans = [] - for i in range(count): - s = stream[i] - if len(s) < 2 or s[0] != "\"" or s[-1] != "\"": - print "Invalid string", s - print "Note: ply_import.py does not handle whitespace in strings" - return None - ans.append(s[1:-1]) - stream[:count] = [] - return ans - if (num_type == 'f' or num_type == 'd'): - mapper = float - else: - mapper = int - ans = map(lambda x: mapper(x), stream[:count]) - stream[:count] = [] - return ans - else: - if (num_type == 's'): - ans = [] - for i in range(count): - fmt = format + "i" - data = stream.read(struct.calcsize(fmt)) - length = struct.unpack(fmt, data)[0] - fmt = format + str(length) + "s" - data = stream.read(struct.calcsize(fmt)) - s = struct.unpack(fmt, data)[0] - ans.append(s[0:-1]) # strip the NULL - return ans - else: - fmt = format + str(count) + num_type - data = stream.read(struct.calcsize(fmt)); - return struct.unpack(fmt, data) + def read_format(self, format, count, num_type, stream): + if format == 'ascii': + if (num_type == 's'): + ans = [] + for i in xrange(count): + s = stream[i] + if len(s) < 2 or s[0] != '"' or s[-1] != '"': + print 'Invalid string', s + print 'Note: ply_import.py does not handle whitespace in strings' + return None + ans.append(s[1:-1]) + stream[:count] = [] + return ans + if (num_type == 'f' or num_type == 'd'): + mapper = float + else: + mapper = int + ans = map(lambda x: mapper(x), stream[:count]) + stream[:count] = [] + return ans + else: + if (num_type == 's'): + ans = [] + for i in xrange(count): + fmt = format + 'i' + data = stream.read(struct.calcsize(fmt)) + length = struct.unpack(fmt, data)[0] + fmt = '%s%is' % (format, length) + data = stream.read(struct.calcsize(fmt)) + s = struct.unpack(fmt, data)[0] + ans.append(s[:-1]) # strip the NULL + return ans + else: + fmt = '%s%i%s' % (format, count, num_type) + data = stream.read(struct.calcsize(fmt)); + return struct.unpack(fmt, data) - def load(self, format, stream): - if (self.list_type != None): - count = int(self.read_format(format, 1, self.list_type, stream)[0]) - return self.read_format(format, count, self.numeric_type, stream) - else: - return self.read_format(format, 1, self.numeric_type, stream)[0] + def load(self, format, stream): + if (self.list_type != None): + count = int(self.read_format(format, 1, self.list_type, stream)[0]) + return self.read_format(format, count, self.numeric_type, stream) + else: + return self.read_format(format, 1, self.numeric_type, stream)[0] class object_spec: - "A list of element_specs" - specs = [] + 'A list of element_specs' + specs = [] - def load(self, format, stream): - answer = {} - for i in self.specs: - answer[i.name] = [] - for j in range(i.count): - if not j % 100 and meshtools.show_progress: - Blender.Window.DrawProgressBar(float(j) / i.count, "Loading " + i.name) - answer[i.name].append(i.load(format, stream)) - return answer + def load(self, format, stream): + return dict([(i.name,[i.load(format, stream) for j in xrange(i.count) ]) for i in self.specs]) + + ''' + answer = {} + for i in self.specs: + answer[i.name] = [] + for j in xrange(i.count): + if not j % 100 and meshtools.show_progress: + Blender.Window.DrawProgressBar(float(j) / i.count, 'Loading ' + i.name) + answer[i.name].append(i.load(format, stream)) + return answer + ''' + def read(filename): - format = "" - version = "1.0" - format_specs = {"binary_little_endian": "<", - "binary_big_endian": ">", - "ascii": "ascii"} - type_specs = {"char": "b", - "uchar": "B", - "int8": "b", - "uint8": "B", - "int16": "h", - "uint16": "H", - "int": "i", - "int32": "i", - "uint": "I", - "uint32": "I", - "float": "f", - "float32": "f", - "float64": "d", - "string": "s"} - obj_spec = object_spec() + format = '' + version = '1.0' + format_specs = {'binary_little_endian': '<', + 'binary_big_endian': '>', + 'ascii': 'ascii'} + type_specs = {'char': 'b', + 'uchar': 'B', + 'int8': 'b', + 'uint8': 'B', + 'int16': 'h', + 'uint16': 'H', + 'int': 'i', + 'int32': 'i', + 'uint': 'I', + 'uint32': 'I', + 'float': 'f', + 'float32': 'f', + 'float64': 'd', + 'string': 's'} + obj_spec = object_spec() - try: - file = open(filename, "rb") - signature = file.readline() - if (signature != "ply\n"): - print "Signature line was invalid" - return None - while 1: - line = file.readline() - tokens = re.split(r'[ \n]+', line) - if (len(tokens) == 0): - continue - if (tokens[0] == 'end_header'): - break - elif (tokens[0] == 'comment' or tokens[0] == 'obj_info'): - continue - elif (tokens[0] == 'format'): - if (len(tokens) < 3): - print "Invalid format line" - return None - if (tokens[1] not in format_specs.keys()): - print "Unknown format " + tokens[1] - return None - if (tokens[2] != version): - print "Unknown version " + tokens[2] - return None - format = tokens[1] - elif (tokens[0] == "element"): - if (len(tokens) < 3): - print "Invalid element line" - return None - obj_spec.specs.append(element_spec(tokens[1], int(tokens[2]))) - elif (tokens[0] == "property"): - if (not len(obj_spec.specs)): - print "Property without element" - return None - if (tokens[1] == "list"): - obj_spec.specs[-1].properties.append(property_spec(tokens[4], type_specs[tokens[2]], type_specs[tokens[3]])) - else: - obj_spec.specs[-1].properties.append(property_spec(tokens[2], None, type_specs[tokens[1]])) - obj = obj_spec.load(format_specs[format], file) + try: + file = open(filename, 'rb') + signature = file.readline() + if (signature != 'ply\n'): + print 'Signature line was invalid' + return None + while 1: + tokens = re.split(r'[ \n]+', file.readline()) + if (len(tokens) == 0): + continue + if (tokens[0] == 'end_header'): + break + elif (tokens[0] == 'comment' or tokens[0] == 'obj_info'): + continue + elif (tokens[0] == 'format'): + if (len(tokens) < 3): + print 'Invalid format line' + return None + if (tokens[1] not in format_specs.keys()): + print 'Unknown format', tokens[1] + return None + if (tokens[2] != version): + print 'Unknown version', tokens[2] + return None + format = tokens[1] + elif (tokens[0] == 'element'): + if (len(tokens) < 3): + print 'Invalid element line' + return None + obj_spec.specs.append(element_spec(tokens[1], int(tokens[2]))) + elif (tokens[0] == 'property'): + if (not len(obj_spec.specs)): + print 'Property without element' + return None + if (tokens[1] == 'list'): + obj_spec.specs[-1].properties.append(property_spec(tokens[4], type_specs[tokens[2]], type_specs[tokens[3]])) + else: + obj_spec.specs[-1].properties.append(property_spec(tokens[2], None, type_specs[tokens[1]])) + obj = obj_spec.load(format_specs[format], file) - except IOError, (errno, strerror): - file.close() - return None + except IOError, (errno, strerror): + file.close() + return None - file.close() - return (obj_spec, obj); + file.close() + return (obj_spec, obj); -def add_face(mesh, vertices, varr, indices, uvindices, colindices): - face = Blender.NMesh.Face() - for index in indices: - vertex = vertices[index]; - face.v.append(varr[index]) - if uvindices: - face.uv.append((vertex[uvindices[0]], 1.0 - vertex[uvindices[1]])) - if colindices: - if not uvindices: face.uv.append((0, 0)) # Force faceUV - face.col.append(Blender.NMesh.Col(vertex[colindices[0]], vertex[colindices[1]], vertex[colindices[2]], 255)) - mesh.faces.append(face) +def add_face(vertices, varr, indices, uvindices, colindices): + face = Blender.NMesh.Face([varr[i] for i in indices]) + for index in indices: + vertex = vertices[index]; + + if uvindices: + face.uv.append((vertex[uvindices[0]], 1.0 - vertex[uvindices[1]])) + if colindices: + if not uvindices: face.uv.append((0, 0)) # Force faceUV + face.col.append(Blender.NMesh.Col(vertex[colindices[0]], vertex[colindices[1]], vertex[colindices[2]], 255)) + return face def filesel_callback(filename): - (obj_spec, obj) = read(filename) - if obj == None: - print "Invalid file" - return - vmap = {} - varr = [] - uvindices = None - noindices = None - colindices = None - for el in obj_spec.specs: - if el.name == "vertex": - vindices = (el.index("x"), el.index("y"), el.index("z")) - if el.index("nx") >= 0 and el.index("ny") >= 0 and el.index("nz") >= 0: - noindices = (el.index("nx"), el.index("ny"), el.index("nz")) - if el.index("s") >= 0 and el.index("t") >= 0: - uvindices = (el.index("s"), el.index("t")) - if el.index("red") >= 0 and el.index("green") and el.index("blue") >= 0: - colindices = (el.index("red"), el.index("green"), el.index("blue")) - elif el.name == "face": - findex = el.index("vertex_indices") + t = Blender.sys.time() + (obj_spec, obj) = read(filename) + if obj == None: + print 'Invalid file' + return + vmap = {} + varr = [] + uvindices = None + noindices = None + colindices = None + for el in obj_spec.specs: + if el.name == 'vertex': + vindices = vindices_x, vindices_y, vindices_z = (el.index('x'), el.index('y'), el.index('z')) + if el.index('nx') >= 0 and el.index('ny') >= 0 and el.index('nz') >= 0: + noindices = (el.index('nx'), el.index('ny'), el.index('nz')) + if el.index('s') >= 0 and el.index('t') >= 0: + uvindices = (el.index('s'), el.index('t')) + if el.index('red') >= 0 and el.index('green') and el.index('blue') >= 0: + colindices = (el.index('red'), el.index('green'), el.index('blue')) + elif el.name == 'face': + findex = el.index('vertex_indices') + - mesh = Blender.NMesh.GetRaw() - for v in obj["vertex"]: - x = v[vindices[0]] - y = v[vindices[1]] - z = v[vindices[2]] - if noindices > 0: - nx = v[noindices[0]] - ny = v[noindices[1]] - nz = v[noindices[2]] - vkey = (x, y, z, nx, ny, nz) - else: - vkey = (x, y, z) - if not vmap.has_key(vkey): - mesh.verts.append(Blender.NMesh.Vert(x, y, z)) - if noindices > 0: - mesh.verts[-1].no[0] = nx - mesh.verts[-1].no[1] = ny - mesh.verts[-1].no[2] = nz - vmap[vkey] = mesh.verts[-1] - varr.append(vmap[vkey]) - for f in obj["face"]: - ind = f[findex] - nind = len(ind) - if nind <= 4: - add_face(mesh, obj["vertex"], varr, ind, uvindices, colindices) - else: - for j in range(nind - 2): - add_face(mesh, obj["vertex"], varr, (ind[0], ind[j + 1], ind[j + 2]), uvindices, colindices) + mesh = Blender.NMesh.GetRaw() + NMVert = Blender.NMesh.Vert + for v in obj['vertex']: + + if noindices > 0: + x,y,z,nx,ny,nz = vkey =\ + (v[vindices_x], v[vindices_y], v[vindices_z],\ + v[noindices[0]], v[noindices[1]], v[noindices[2]]) + else: + x,y,z = vkey = (v[vindices_x], v[vindices_y], v[vindices_z]) + #if not vmap.has_key(vkey): + try: # try uses 1 less dict lookup + varr.append(vmap[vkey]) + except: + nmv = NMVert(vkey[0], vkey[1], vkey[2]) + mesh.verts.append(nmv) + if noindices > 0: + nmv.no[0] = vkey[3] + nmv.no[1] = vkey[4] + nmv.no[2] = vkey[5] + vmap[vkey] = nmv + varr.append(vmap[vkey]) + + verts = obj['vertex'] + for f in obj['face']: + ind = f[findex] + nind = len(ind) + if nind <= 4: + mesh.faces.append(add_face(verts, varr, ind, uvindices, colindices)) + else: + for j in xrange(nind - 2): + mesh.faces.append(add_face(verts, varr, (ind[0], ind[j + 1], ind[j + 2]), uvindices, colindices)) + + del obj # Reclaim memory - obj = None # Reclaim memory + if noindices: + normals = 1 + else: + normals = 0 + objname = Blender.sys.splitext(Blender.sys.basename(filename))[0] + if not meshtools.overwrite_mesh_name: + objname = meshtools.versioned_name(objname) + Blender.NMesh.PutRaw(mesh, objname, not normals) + Blender.Object.GetSelected()[0].name = objname + Blender.Redraw() + Blender.Window.DrawProgressBar(1.0, '') + message = 'Successfully imported ' + Blender.sys.basename(filename) + ' ' + str(Blender.sys.time()-t) + meshtools.print_boxed(message) - if noindices: - normals = 1 - else: - normals = 0 - objname = Blender.sys.splitext(Blender.sys.basename(filename))[0] - if not meshtools.overwrite_mesh_name: - objname = meshtools.versioned_name(objname) - Blender.NMesh.PutRaw(mesh, objname, not normals) - Blender.Object.GetSelected()[0].name = objname - Blender.Redraw() - Blender.Window.DrawProgressBar(1.0, '') - message = "Successfully imported " + Blender.sys.basename(filename) - meshtools.print_boxed(message) - -Blender.Window.FileSelector(filesel_callback, "Import PLY") +Blender.Window.FileSelector(filesel_callback, 'Import PLY')