1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
25 # may split this out into a new file
26 def replace_bpy_app_version():
27 """ So MD5's are predictable from output which uses blenders versions.
33 app_fake = type(bpy)("bpy.app")
36 if not attr.startswith("_"):
37 setattr(app_fake, attr, getattr(app, attr))
39 app_fake.version = 0, 0, 0
40 app_fake.version_string = "0.00 (sub 0)"
44 def clear_startup_blend():
47 for scene in bpy.data.scenes:
48 for obj in scene.objects:
49 scene.objects.unlink(obj)
54 scene = bpy.context.scene
57 def matrix2str(matrix):
58 return "".join([str(round(axis, ROUND)) for vector in matrix for axis in vector]).encode('ASCII')
60 def coords2str(seq, attr):
61 return "".join([str(round(axis, ROUND)) for vertex in seq for axis in getattr(vertex, attr)]).encode('ASCII')
65 md5 = hashlib.new("md5")
66 md5_update = md5.update
68 for obj in scene.objects:
69 md5_update(matrix2str(obj.matrix_world))
72 if type(data) == bpy.types.Mesh:
73 md5_update(coords2str(data.vertices, "co"))
74 elif type(data) == bpy.types.Curve:
75 for spline in data.splines:
76 md5_update(coords2str(spline.bezier_points, "co"))
77 md5_update(coords2str(spline.points, "co"))
79 return md5.hexdigest()
84 print(" args:", " ".join(argv))
85 argv = argv[argv.index("--") + 1:]
87 def arg_extract(arg, optional=True, array=False):
96 if argv[i].startswith(arg):
97 item = argv[i][len(arg):]
109 if (not value) and (not optional):
110 print(" '%s' not set" % arg)
115 run = arg_extract("--run", optional=False)
116 md5 = arg_extract("--md5", optional=False)
117 md5_method = arg_extract("--md5_method", optional=False) # 'SCENE' / 'FILE'
119 # only when md5_method is 'FILE'
120 md5_source = arg_extract("--md5_source", optional=True, array=True)
122 # save blend file, for testing
123 write_blend = arg_extract("--write-blend", optional=True)
125 # ensure files are written anew
127 if os.path.exists(f):
132 replace_bpy_app_version()
133 if not bpy.data.filepath:
134 clear_startup_blend()
136 print(" Running: '%s'" % run)
137 print(" MD5: '%s'!" % md5)
143 traceback.print_exc()
146 if write_blend is not None:
147 print(" Writing Blend: %s" % write_blend)
148 bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=write_blend)
150 print(" Result: '%s'" % str(result))
152 print(" Running: %s -> False" % run)
155 if md5_method == 'SCENE':
156 md5_new = blend_to_md5()
157 elif md5_method == 'FILE':
159 print(" Missing --md5_source argument")
163 if not os.path.exists(f):
164 print(" Missing --md5_source=%r argument does not point to a file")
169 md5_instance = hashlib.new("md5")
170 md5_update = md5_instance.update
173 filehandle = open(f, "rb")
174 md5_update(filehandle.read())
177 md5_new = md5_instance.hexdigest()
180 print(" Invalid --md5_method=%s argument is not a valid source")
184 print(" Running: %s\n MD5 Recieved: %s\n MD5 Expected: %s" % (run, md5_new, md5))
187 print(" Success: %s" % run)
190 if __name__ == "__main__":
191 # So a python error exits(1)
196 traceback.print_exc()