|  |  | @@ -0,0 +1,245 @@ | 
		
	
		
			
			|  |  |  | import bpy | 
		
	
		
			
			|  |  |  | from mathutils import Vector, Euler, Color | 
		
	
		
			
			|  |  |  | import json | 
		
	
		
			
			|  |  |  | import pathlib | 
		
	
		
			
			|  |  |  | import os | 
		
	
		
			
			|  |  |  | from math import pi | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | VIEW_DATA = { | 
		
	
		
			
			|  |  |  | "Front": [0, 1, 2, "Front"], | 
		
	
		
			
			|  |  |  | "Angled": [0.25, 1, 2, "Angled"], | 
		
	
		
			
			|  |  |  | "Corner": [0.5, 1, 2, "Corner"], | 
		
	
		
			
			|  |  |  | "Side": [1, 1, 2, "Side"], | 
		
	
		
			
			|  |  |  | "Back Angled": [1.5, 1, 2, "Back Angled"], | 
		
	
		
			
			|  |  |  | "Back": [2, 1, 2, "Back"], | 
		
	
		
			
			|  |  |  | "Top": [0, 0, 1, "Top"], | 
		
	
		
			
			|  |  |  | "Bottom": [0, 2, 1, "Bottom"], | 
		
	
		
			
			|  |  |  | "Bottom Flipped": [2, 2, 1, "Bottom Flipped"], | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def get_bounds(objects): | 
		
	
		
			
			|  |  |  | xl = [] | 
		
	
		
			
			|  |  |  | yl = [] | 
		
	
		
			
			|  |  |  | zl = [] | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | for obj in objects: | 
		
	
		
			
			|  |  |  | if not obj.hide_render: | 
		
	
		
			
			|  |  |  | for bounds in obj.bound_box: | 
		
	
		
			
			|  |  |  | v = obj.matrix_world @ Vector(bounds) | 
		
	
		
			
			|  |  |  | xl += [v[0] for c in obj.bound_box] | 
		
	
		
			
			|  |  |  | yl += [v[1] for c in obj.bound_box] | 
		
	
		
			
			|  |  |  | zl += [v[2] for c in obj.bound_box] | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | return ( | 
		
	
		
			
			|  |  |  | Vector([min(xl), min(yl), min(zl)]), | 
		
	
		
			
			|  |  |  | Vector([max(xl), max(yl), max(zl)]) | 
		
	
		
			
			|  |  |  | ) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | class MVConfigCollection(bpy.types.Operator): | 
		
	
		
			
			|  |  |  | bl_idname = "mv.config_collection" | 
		
	
		
			
			|  |  |  | bl_label = "Configure root collection" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | @classmethod | 
		
	
		
			
			|  |  |  | def poll(cls, context: bpy.types.Context): | 
		
	
		
			
			|  |  |  | return True | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | def execute(self, context: bpy.types.Context): | 
		
	
		
			
			|  |  |  | coll = context.scene.collection.children[0] | 
		
	
		
			
			|  |  |  | coll.name = "Macrovision" | 
		
	
		
			
			|  |  |  | coll["MVName"] = "Name" | 
		
	
		
			
			|  |  |  | coll["MVViews"] = "Front" | 
		
	
		
			
			|  |  |  | coll["MVKind"] = "objects" | 
		
	
		
			
			|  |  |  | coll["MVViewLabels"] = "Front: Front" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | mats = [ | 
		
	
		
			
			|  |  |  | ("light", 0, 0, 1), | 
		
	
		
			
			|  |  |  | ("medium", 0, 1, 0), | 
		
	
		
			
			|  |  |  | ("dark", 1, 0, 0) | 
		
	
		
			
			|  |  |  | ] | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | for name, red, green, blue in mats: | 
		
	
		
			
			|  |  |  | if name not in bpy.data.materials: | 
		
	
		
			
			|  |  |  | bpy.data.materials.new(name) | 
		
	
		
			
			|  |  |  | mat = bpy.data.materials[name] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | mat.use_nodes = True | 
		
	
		
			
			|  |  |  | mat.use_fake_user = True | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | mat.node_tree.nodes.clear() | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | rgb = mat.node_tree.nodes.new("ShaderNodeRGB") | 
		
	
		
			
			|  |  |  | out = mat.node_tree.nodes.new("ShaderNodeOutputMaterial") | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | input = out.inputs['Surface'] | 
		
	
		
			
			|  |  |  | output = rgb.outputs['Color'] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | mat.node_tree.links.new(input=input, output=output) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | output.default_value = (red, green, blue, 1.0) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if "cam" not in bpy.data.objects: | 
		
	
		
			
			|  |  |  | new_cam = bpy.data.cameras.new("cam") | 
		
	
		
			
			|  |  |  | new_obj = bpy.data.objects.new("cam", new_cam) | 
		
	
		
			
			|  |  |  | context.scene.collection.objects.link(new_obj) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if "Lines" not in bpy.data.materials: | 
		
	
		
			
			|  |  |  | mat = bpy.data.materials.new("Lines") | 
		
	
		
			
			|  |  |  | bpy.data.materials.create_gpencil_data(mat) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if "lineart" not in bpy.data.objects: | 
		
	
		
			
			|  |  |  | new_lineart = bpy.data.grease_pencils.new("lineart") | 
		
	
		
			
			|  |  |  | new_obj = bpy.data.objects.new("lineart", new_lineart) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | new_obj.show_in_front = True | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | context.scene.collection.objects.link(new_obj) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | modifier = new_obj.grease_pencil_modifiers.new(name='Lineart', type='GP_LINEART') | 
		
	
		
			
			|  |  |  | material = bpy.data.materials["Lines"] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | new_lineart.materials.append(material) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | modifier.target_layer = "Lines" | 
		
	
		
			
			|  |  |  | modifier.target_material = material | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | new_lineart.layers.new("Lines") | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | # we have to clear the bake, for some reason | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | bpy.ops.object.lineart_clear_all() | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return {"FINISHED"} | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | class MVExport(bpy.types.Operator): | 
		
	
		
			
			|  |  |  | bl_idname = "mv.export" | 
		
	
		
			
			|  |  |  | bl_label = "Export objects" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | @classmethod | 
		
	
		
			
			|  |  |  | def poll(cls, context: bpy.types.Context): | 
		
	
		
			
			|  |  |  | return True | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | def execute(self, context: bpy.types.Context): | 
		
	
		
			
			|  |  |  | path_info = pathlib.Path(bpy.data.filepath).parent.joinpath("macrovision-directory.txt") | 
		
	
		
			
			|  |  |  | config_path = pathlib.Path(open(path_info).read().strip()) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | json_path = config_path.joinpath("config.json") | 
		
	
		
			
			|  |  |  | config = json.load(open(json_path.resolve(), encoding="utf-8")) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | parent_workdir = config["work-directory"] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | c = bpy.data.objects["cam"] | 
		
	
		
			
			|  |  |  | context.scene.camera = c | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lineart = bpy.data.objects["lineart"] | 
		
	
		
			
			|  |  |  | lineart.grease_pencil_modifiers['Lineart'].source_type = 'COLLECTION' | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | c.data.type = "ORTHO" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | bpy.data.scenes["Scene"].render.resolution_x = 2000 | 
		
	
		
			
			|  |  |  | bpy.data.scenes["Scene"].render.resolution_y = 2000 | 
		
	
		
			
			|  |  |  | bpy.data.scenes["Scene"].render.film_transparent = True | 
		
	
		
			
			|  |  |  | bpy.data.scenes["Scene"].view_settings.view_transform = "Raw" | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[1].default_value = 0 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | mv = bpy.data.collections["Macrovision"] | 
		
	
		
			
			|  |  |  | collections = mv.children | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | all_data = {} | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | all_data["name"] = mv["MVName"] | 
		
	
		
			
			|  |  |  | all_data["kind"] = mv["MVKind"] | 
		
	
		
			
			|  |  |  | all_data["forms"] = [] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | default_views = [] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | for view in mv["MVViews"].split(","): | 
		
	
		
			
			|  |  |  | default_views.append(VIEW_DATA[view.strip()]) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if "MVViewLabels" in mv: | 
		
	
		
			
			|  |  |  | for pair in mv["MVViewLabels"].split(","): | 
		
	
		
			
			|  |  |  | key, val = pair.split(":") | 
		
	
		
			
			|  |  |  | VIEW_DATA[key.strip()][3] = val.strip() | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | workdir = pathlib.Path(parent_workdir).joinpath(all_data["name"]) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | os.makedirs(workdir, exist_ok=True) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | for coll in collections: | 
		
	
		
			
			|  |  |  | coll.hide_render = True | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | for coll in collections: | 
		
	
		
			
			|  |  |  | coll.hide_render = False | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | bpy.ops.object.select_all(action='DESELECT') | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | for obj in coll.objects: | 
		
	
		
			
			|  |  |  | obj.select_set(True) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | data = {} | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | data["name"] = coll.name | 
		
	
		
			
			|  |  |  | data["views"] = [] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | bound_min, bound_max = get_bounds(coll.objects) | 
		
	
		
			
			|  |  |  | dimensions = bound_max - bound_min | 
		
	
		
			
			|  |  |  | size = max(dimensions) | 
		
	
		
			
			|  |  |  | global_bbox_center = 0.5 * (bound_min + bound_max) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | view_list = [] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lineart.grease_pencil_modifiers['Lineart'].source_collection = coll | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | if "Views" in coll: | 
		
	
		
			
			|  |  |  | for view in coll["Views"].split(","): | 
		
	
		
			
			|  |  |  | view_list.append(VIEW_DATA[view]) | 
		
	
		
			
			|  |  |  | else: | 
		
	
		
			
			|  |  |  | view_list = default_views | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | for angles in view_list: | 
		
	
		
			
			|  |  |  | c.location = global_bbox_center | 
		
	
		
			
			|  |  |  | c.rotation_euler = Euler([angles[1] * pi / 2, 0, angles[0] * pi / 2]) | 
		
	
		
			
			|  |  |  | print(list(bound_min) + list(bound_max)) | 
		
	
		
			
			|  |  |  | _, c.data.ortho_scale = c.camera_fit_coords(bpy.context.evaluated_depsgraph_get(), list(bound_min) + list(bound_max)) | 
		
	
		
			
			|  |  |  | c.location = Vector([c.location[0], c.location[1], c.location[2]]) | 
		
	
		
			
			|  |  |  | c.data.ortho_scale *= 1.2 | 
		
	
		
			
			|  |  |  | rot = c.rotation_euler.to_matrix() | 
		
	
		
			
			|  |  |  | rot.invert() | 
		
	
		
			
			|  |  |  | c.location += Vector([0, 0, size * 2]) @ rot | 
		
	
		
			
			|  |  |  | c.data.clip_start = size / 4 | 
		
	
		
			
			|  |  |  | c.data.clip_end = size * 8 | 
		
	
		
			
			|  |  |  | data["views"].append({ | 
		
	
		
			
			|  |  |  | "name": angles[3], | 
		
	
		
			
			|  |  |  | "height": dimensions[angles[2]] | 
		
	
		
			
			|  |  |  | }) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | if "Volume" in coll: | 
		
	
		
			
			|  |  |  | data["views"][-1]["volume"] = coll["Volume"] | 
		
	
		
			
			|  |  |  | if "Mass" in coll: | 
		
	
		
			
			|  |  |  | data["views"][-1]["mass"] = coll["Mass"] | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | lineart.hide_render = False | 
		
	
		
			
			|  |  |  | filename = f"{coll.name}-{angles[3]}.png" | 
		
	
		
			
			|  |  |  | bpy.context.scene.render.filepath = workdir.joinpath(filename).resolve().__str__() | 
		
	
		
			
			|  |  |  | bpy.ops.render.render(write_still = True) | 
		
	
		
			
			|  |  |  | lineart.hide_render = True | 
		
	
		
			
			|  |  |  | filename = f"{coll.name}-{angles[3]}-noline.png" | 
		
	
		
			
			|  |  |  | bpy.context.scene.render.filepath = workdir.joinpath(filename).resolve().__str__() | 
		
	
		
			
			|  |  |  | bpy.ops.render.render(write_still = True) | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | all_data["forms"].append(data) | 
		
	
		
			
			|  |  |  | coll.hide_render = True | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | with open(workdir.joinpath("data.json"), "w") as file: | 
		
	
		
			
			|  |  |  | json.dump(all_data, file) | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | return {"FINISHED"} | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | clses = [ | 
		
	
		
			
			|  |  |  | MVExport, | 
		
	
		
			
			|  |  |  | MVConfigCollection | 
		
	
		
			
			|  |  |  | ] |