| @@ -0,0 +1,3 @@ | |||
| { | |||
| "python.formatting.provider": "black" | |||
| } | |||
| @@ -1,26 +0,0 @@ | |||
| import mv.ui | |||
| import mv.ops | |||
| import bpy | |||
| import importlib | |||
| bl_info = { | |||
| "name": "Macrovision", | |||
| "blender": (3, 0, 0), | |||
| "category": "Import-Export" | |||
| } | |||
| def register(): | |||
| importlib.reload(mv.ui) | |||
| importlib.reload(mv.ops) | |||
| cls_lists = [mv.ops.clses, mv.ui.clses] | |||
| for cls_list in cls_lists: | |||
| for cls in cls_list: | |||
| bpy.utils.register_class(cls) | |||
| def unregister(): | |||
| cls_lists = [mv.ops.clses, mv.ui.clses][::-1] | |||
| for cls_list in cls_lists[::-1]: | |||
| for cls in cls_list: | |||
| bpy.utils.register_class(cls) | |||
| @@ -0,0 +1,33 @@ | |||
| from macrovision import ui, ops, props | |||
| import bpy | |||
| import importlib | |||
| bl_info = { | |||
| "name": "Macrovision", | |||
| "blender": (3, 0, 0), | |||
| "category": "Import-Export" | |||
| } | |||
| def register(): | |||
| importlib.reload(ui) | |||
| importlib.reload(ops) | |||
| importlib.reload(props) | |||
| cls_lists = [props.clses, ops.clses, ui.clses] | |||
| for cls_list in cls_lists: | |||
| for cls in cls_list: | |||
| bpy.utils.register_class(cls) | |||
| for id, prop in props.scene_props.items(): | |||
| setattr(bpy.types.Scene, id, prop) | |||
| def unregister(): | |||
| cls_lists = [props.clses, ops.clses, ui.clses][::-1] | |||
| for cls_list in cls_lists[::-1]: | |||
| for cls in cls_list: | |||
| bpy.utils.unregister_class(cls) | |||
| for id in props.scene_props: | |||
| if id in props.scene_props: | |||
| delattr(bpy.types.Scene, id) | |||
| @@ -1,20 +1,22 @@ | |||
| import bpy | |||
| from mathutils import Vector, Euler, Color | |||
| import json | |||
| import pathlib | |||
| import os | |||
| from math import pi | |||
| import random | |||
| 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"], | |||
| "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): | |||
| @@ -46,10 +48,6 @@ class MVConfigCollection(bpy.types.Operator): | |||
| 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), | |||
| @@ -109,18 +107,61 @@ class MVConfigCollection(bpy.types.Operator): | |||
| bpy.ops.object.lineart_clear_all() | |||
| return {"FINISHED"} | |||
| return {'FINISHED'} | |||
| class MVAssignMaterials(bpy.types.Operator): | |||
| bl_idname = "mv.assign_materials" | |||
| bl_label = "Assign Materials" | |||
| def execute(self, context: bpy.types.Context): | |||
| mv = bpy.data.collections["Macrovision"] | |||
| collections = mv.children | |||
| for coll in collections: | |||
| for object in coll.objects: | |||
| if object.type != "MESH": | |||
| continue | |||
| for index in range(len(object.material_slots)): | |||
| if object.material_slots[index].material.name in ('light', 'medium', 'dark'): | |||
| continue | |||
| if context.scene.mv_material_mode == 'RANDOM': | |||
| choices = [ | |||
| bpy.data.materials['light'], | |||
| bpy.data.materials['medium'], | |||
| bpy.data.materials['dark'] | |||
| ] | |||
| object.material_slots[index].material = random.choice(choices) | |||
| if context.scene.mv_material_mode == 'NAMES': | |||
| material = object.material_slots[index].material | |||
| light_choices = context.scene.mv_material_names_light.split(",") | |||
| medium_choices = context.scene.mv_material_names_medium.split(",") | |||
| chosen = None | |||
| for choice in light_choices: | |||
| if choice in material.name or choice in object.name: | |||
| chosen = bpy.data.materials['light'] | |||
| break | |||
| for choice in medium_choices: | |||
| if choice in material.name or choice in object.name: | |||
| chosen = bpy.data.materials['medium'] | |||
| break | |||
| if chosen is None: | |||
| chosen = bpy.data.materials['dark'] | |||
| object.material_slots[index].material = chosen | |||
| 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()) | |||
| @@ -150,20 +191,21 @@ class MVExport(bpy.types.Operator): | |||
| all_data = {} | |||
| all_data["name"] = mv["MVName"] | |||
| all_data["kind"] = mv["MVKind"] | |||
| all_data["name"] = context.scene.mv_name | |||
| all_data["kind"] = context.scene.mv_kind | |||
| 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() | |||
| for view in context.scene.mv_views: | |||
| key = view.view | |||
| default_views.append([ | |||
| VIEW_DATA[key][0], | |||
| VIEW_DATA[key][1], | |||
| VIEW_DATA[key][2], | |||
| view.name if view.name != "" else VIEW_DATA[key][3] | |||
| ]) | |||
| print(default_views) | |||
| workdir = pathlib.Path(parent_workdir).joinpath(all_data["name"]) | |||
| @@ -212,15 +254,19 @@ class MVExport(bpy.types.Operator): | |||
| c.location += Vector([0, 0, size * 2]) @ rot | |||
| c.data.clip_start = size / 4 | |||
| c.data.clip_end = size * 8 | |||
| scale_factor = (10 ** context.scene.mv_scale_factor) | |||
| height = dimensions[angles[2]] * scale_factor | |||
| data["views"].append({ | |||
| "name": angles[3], | |||
| "height": dimensions[angles[2]] | |||
| "height": height | |||
| }) | |||
| if "Volume" in coll: | |||
| data["views"][-1]["volume"] = coll["Volume"] | |||
| data["views"][-1]["volume"] = coll["Volume"] * (scale_factor ** 3) | |||
| if "Mass" in coll: | |||
| data["views"][-1]["mass"] = coll["Mass"] | |||
| data["views"][-1]["mass"] = coll["Mass"] * (scale_factor ** 3) | |||
| lineart.hide_render = False | |||
| filename = f"{coll.name}-{angles[3]}.png" | |||
| @@ -237,9 +283,10 @@ class MVExport(bpy.types.Operator): | |||
| with open(workdir.joinpath("data.json"), "w") as file: | |||
| json.dump(all_data, file) | |||
| return {"FINISHED"} | |||
| return {'FINISHED'} | |||
| clses = [ | |||
| MVExport, | |||
| MVAssignMaterials, | |||
| MVConfigCollection | |||
| ] | |||
| @@ -0,0 +1,77 @@ | |||
| import bpy | |||
| scene_props = {} | |||
| scene_props["mv_name"] = bpy.props.StringProperty( | |||
| name = "Name", | |||
| description = "The name of the thing", | |||
| default = "" | |||
| ) | |||
| scene_props["mv_kind"] = bpy.props.StringProperty( | |||
| name = "Kind", | |||
| description = "What category the entity belongs to", | |||
| default = "objects" | |||
| ) | |||
| class MVView(bpy.types.PropertyGroup): | |||
| view: bpy.props.EnumProperty( | |||
| name = "View", | |||
| description = "The angle to view from", | |||
| items = ( | |||
| ("FRONT", "Front", ""), | |||
| ("ANGLED", "Angled", ""), | |||
| ("CORNER", "Corner", ""), | |||
| ("SIDE", "Side", ""), | |||
| ("BACK", "Back", ""), | |||
| ("TOP", "Top", ""), | |||
| ("BOTTOM", "Bottom", "") | |||
| ) | |||
| ) | |||
| name: bpy.props.StringProperty(name = "Name", description="What to call this view") | |||
| scene_props["mv_views"] = bpy.props.CollectionProperty( | |||
| name = "Views", | |||
| description = "The views to view the object from", | |||
| type = MVView | |||
| ) | |||
| scene_props["mv_views_index"] = bpy.props.IntProperty( | |||
| name = "Views index", | |||
| description = "View list index" | |||
| ) | |||
| scene_props["mv_scale_factor"] = bpy.props.IntProperty( | |||
| name = "Scale Factor", | |||
| description = "Scale lengths by 10^x", | |||
| min = -20, | |||
| max = 20, | |||
| soft_min = -20, | |||
| soft_max = 20, | |||
| default = 5 | |||
| ) | |||
| scene_props["mv_material_mode"] = bpy.props.EnumProperty( | |||
| name = "Material Mode", | |||
| description = "How to decide what to replace each material with", | |||
| items = ( | |||
| ('RANDOM', "Random", 'Randomly assign materials'), | |||
| ('NAMES', "Names", 'Turn the specified names medium or light; the rest become dark') | |||
| ), | |||
| ) | |||
| scene_props["mv_material_names_light"] = bpy.props.StringProperty( | |||
| name = "Light Colors", | |||
| description = "Materials containing these words will become light", | |||
| default = "" | |||
| ) | |||
| scene_props["mv_material_names_medium"] = bpy.props.StringProperty( | |||
| name = "Medium Colors", | |||
| description = "Materials containing these words will become medium", | |||
| default = "" | |||
| ) | |||
| clses = [ | |||
| MVView | |||
| ] | |||
| @@ -0,0 +1,92 @@ | |||
| from macrovision import ops | |||
| import bpy | |||
| class MV_UL_ViewList(bpy.types.UIList): | |||
| def draw_item(self, context, layout: bpy.types.UILayout, data, item, icon, active_data, active_propname, index): | |||
| if self.layout_type in {'DEFAULT', 'COMPACT'}: | |||
| layout.prop(item, "view", text="") | |||
| layout.prop(item, "name", text="") | |||
| elif self.layout_type in {'GRID'}: | |||
| layout.alignment = 'CENTER' | |||
| layout.label(text='', icon = 'OBJECT_DATAMODE') | |||
| class MV_Views_List_Add(bpy.types.Operator): | |||
| bl_idname = "mv.views_list_add" | |||
| bl_label = "Add view" | |||
| def execute(self, context: bpy.types.Context): | |||
| context.scene.mv_views.add() | |||
| return {'FINISHED'} | |||
| class MV_Views_List_Delete(bpy.types.Operator): | |||
| bl_idname = "mv.views_list_delete" | |||
| bl_label = "Delete view" | |||
| @classmethod | |||
| def poll(self, context: bpy.types.Context): | |||
| return context.scene.mv_views | |||
| def execute(self, context: bpy.types.Context): | |||
| lst = context.scene.mv_views | |||
| index = context.scene.mv_views_index | |||
| lst.remove(index) | |||
| index = max(0, index - 1) | |||
| index = min(index, len(lst) - 1) | |||
| context.scene.mv_views_index = index | |||
| return {'FINISHED'} | |||
| class MVPanel(bpy.types.Panel): | |||
| bl_idname="OBJECT_PT_MV_menu" | |||
| bl_label="Macrovision" | |||
| bl_space_type="VIEW_3D" | |||
| bl_region_type="UI" | |||
| bl_category = "Macrovision" | |||
| @classmethod | |||
| def poll(cls, context): | |||
| return True | |||
| def draw(self, context): | |||
| layout = self.layout | |||
| box = layout.box() | |||
| box.label(text="Setup") | |||
| op_props = box.operator("mv.config_collection") | |||
| op_props = box.operator("mv.assign_materials") | |||
| box.prop(context.scene, "mv_material_mode") | |||
| if context.scene.mv_material_mode == "NAMES": | |||
| box.prop(context.scene, "mv_material_names_light") | |||
| box.prop(context.scene, "mv_material_names_medium") | |||
| box = layout.box() | |||
| box.label(text="Execute") | |||
| op_props = box.operator("mv.export") | |||
| box.prop(context.scene, "mv_name") | |||
| box.prop(context.scene, "mv_kind") | |||
| box.template_list( | |||
| "MV_UL_ViewList", | |||
| "Views", | |||
| context.scene, | |||
| "mv_views", | |||
| context.scene, | |||
| "mv_views_index" | |||
| ) | |||
| row = box.row() | |||
| row.operator("mv.views_list_add", text="+") | |||
| row.operator("mv.views_list_delete", text="-") | |||
| box.prop(context.scene, 'mv_scale_factor') | |||
| clses = [ | |||
| MV_UL_ViewList, | |||
| MV_Views_List_Add, | |||
| MV_Views_List_Delete, | |||
| MVPanel | |||
| ] | |||
| @@ -1,31 +0,0 @@ | |||
| import mv.ops | |||
| import bpy | |||
| class MVPanel(bpy.types.Panel): | |||
| bl_idname="OBJECT_PT_MV_menu" | |||
| bl_label="Macrovision" | |||
| bl_space_type="VIEW_3D" | |||
| bl_region_type="UI" | |||
| bl_category = "Macrovision" | |||
| @classmethod | |||
| def poll(cls, context): | |||
| return True | |||
| def draw(self, context): | |||
| layout = self.layout | |||
| box = layout.box() | |||
| box.label(text="Setup") | |||
| box.operator("mv.config_collection") | |||
| box = layout.box() | |||
| box.label(text="Execute") | |||
| box.operator("mv.export") | |||
| clses = [ | |||
| MVPanel | |||
| ] | |||