ソースを参照

Work on a proper Blender add-on

master
Fen Dweller 3年前
コミット
c2b9b9f093
5個のファイルの変更304行の追加1行の削除
  1. +2
    -1
      .gitignore
  2. +26
    -0
      scripts/blender/addons/macrovision.py
  3. +0
    -0
      scripts/blender/addons/mv/__init__.py
  4. +245
    -0
      scripts/blender/addons/mv/ops.py
  5. +31
    -0
      scripts/blender/addons/mv/ui.py

+ 2
- 1
.gitignore ファイルの表示

@@ -1 +1,2 @@
illustrator/
illustrator/
__pycache__/

+ 26
- 0
scripts/blender/addons/macrovision.py ファイルの表示

@@ -0,0 +1,26 @@
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
scripts/blender/addons/mv/__init__.py ファイルの表示


+ 245
- 0
scripts/blender/addons/mv/ops.py ファイルの表示

@@ -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
]

+ 31
- 0
scripts/blender/addons/mv/ui.py ファイルの表示

@@ -0,0 +1,31 @@
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
]

読み込み中…
キャンセル
保存