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