less copy protection, more size visualization
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

245 行
7.8 KiB

  1. import bpy
  2. from mathutils import Vector, Euler, Color
  3. import json
  4. import pathlib
  5. import os
  6. from math import pi
  7. VIEW_DATA = {
  8. "Front": [0, 1, 2, "Front"],
  9. "Angled": [0.25, 1, 2, "Angled"],
  10. "Corner": [0.5, 1, 2, "Corner"],
  11. "Side": [1, 1, 2, "Side"],
  12. "Back Angled": [1.5, 1, 2, "Back Angled"],
  13. "Back": [2, 1, 2, "Back"],
  14. "Top": [0, 0, 1, "Top"],
  15. "Bottom": [0, 2, 1, "Bottom"],
  16. "Bottom Flipped": [2, 2, 1, "Bottom Flipped"],
  17. }
  18. def get_bounds(objects):
  19. xl = []
  20. yl = []
  21. zl = []
  22. for obj in objects:
  23. if not obj.hide_render:
  24. for bounds in obj.bound_box:
  25. v = obj.matrix_world @ Vector(bounds)
  26. xl += [v[0] for c in obj.bound_box]
  27. yl += [v[1] for c in obj.bound_box]
  28. zl += [v[2] for c in obj.bound_box]
  29. return (
  30. Vector([min(xl), min(yl), min(zl)]),
  31. Vector([max(xl), max(yl), max(zl)])
  32. )
  33. class MVConfigCollection(bpy.types.Operator):
  34. bl_idname = "mv.config_collection"
  35. bl_label = "Configure root collection"
  36. @classmethod
  37. def poll(cls, context: bpy.types.Context):
  38. return True
  39. def execute(self, context: bpy.types.Context):
  40. coll = context.scene.collection.children[0]
  41. coll.name = "Macrovision"
  42. coll["MVName"] = "Name"
  43. coll["MVViews"] = "Front"
  44. coll["MVKind"] = "objects"
  45. coll["MVViewLabels"] = "Front: Front"
  46. mats = [
  47. ("light", 0, 0, 1),
  48. ("medium", 0, 1, 0),
  49. ("dark", 1, 0, 0)
  50. ]
  51. for name, red, green, blue in mats:
  52. if name not in bpy.data.materials:
  53. bpy.data.materials.new(name)
  54. mat = bpy.data.materials[name]
  55. mat.use_nodes = True
  56. mat.use_fake_user = True
  57. mat.node_tree.nodes.clear()
  58. rgb = mat.node_tree.nodes.new("ShaderNodeRGB")
  59. out = mat.node_tree.nodes.new("ShaderNodeOutputMaterial")
  60. input = out.inputs['Surface']
  61. output = rgb.outputs['Color']
  62. mat.node_tree.links.new(input=input, output=output)
  63. output.default_value = (red, green, blue, 1.0)
  64. if "cam" not in bpy.data.objects:
  65. new_cam = bpy.data.cameras.new("cam")
  66. new_obj = bpy.data.objects.new("cam", new_cam)
  67. context.scene.collection.objects.link(new_obj)
  68. if "Lines" not in bpy.data.materials:
  69. mat = bpy.data.materials.new("Lines")
  70. bpy.data.materials.create_gpencil_data(mat)
  71. if "lineart" not in bpy.data.objects:
  72. new_lineart = bpy.data.grease_pencils.new("lineart")
  73. new_obj = bpy.data.objects.new("lineart", new_lineart)
  74. new_obj.show_in_front = True
  75. context.scene.collection.objects.link(new_obj)
  76. modifier = new_obj.grease_pencil_modifiers.new(name='Lineart', type='GP_LINEART')
  77. material = bpy.data.materials["Lines"]
  78. new_lineart.materials.append(material)
  79. modifier.target_layer = "Lines"
  80. modifier.target_material = material
  81. new_lineart.layers.new("Lines")
  82. # we have to clear the bake, for some reason
  83. bpy.ops.object.lineart_clear_all()
  84. return {"FINISHED"}
  85. class MVExport(bpy.types.Operator):
  86. bl_idname = "mv.export"
  87. bl_label = "Export objects"
  88. @classmethod
  89. def poll(cls, context: bpy.types.Context):
  90. return True
  91. def execute(self, context: bpy.types.Context):
  92. path_info = pathlib.Path(bpy.data.filepath).parent.joinpath("macrovision-directory.txt")
  93. config_path = pathlib.Path(open(path_info).read().strip())
  94. json_path = config_path.joinpath("config.json")
  95. config = json.load(open(json_path.resolve(), encoding="utf-8"))
  96. parent_workdir = config["work-directory"]
  97. c = bpy.data.objects["cam"]
  98. context.scene.camera = c
  99. lineart = bpy.data.objects["lineart"]
  100. lineart.grease_pencil_modifiers['Lineart'].source_type = 'COLLECTION'
  101. c.data.type = "ORTHO"
  102. bpy.data.scenes["Scene"].render.resolution_x = 2000
  103. bpy.data.scenes["Scene"].render.resolution_y = 2000
  104. bpy.data.scenes["Scene"].render.film_transparent = True
  105. bpy.data.scenes["Scene"].view_settings.view_transform = "Raw"
  106. bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[1].default_value = 0
  107. mv = bpy.data.collections["Macrovision"]
  108. collections = mv.children
  109. all_data = {}
  110. all_data["name"] = mv["MVName"]
  111. all_data["kind"] = mv["MVKind"]
  112. all_data["forms"] = []
  113. default_views = []
  114. for view in mv["MVViews"].split(","):
  115. default_views.append(VIEW_DATA[view.strip()])
  116. if "MVViewLabels" in mv:
  117. for pair in mv["MVViewLabels"].split(","):
  118. key, val = pair.split(":")
  119. VIEW_DATA[key.strip()][3] = val.strip()
  120. workdir = pathlib.Path(parent_workdir).joinpath(all_data["name"])
  121. os.makedirs(workdir, exist_ok=True)
  122. for coll in collections:
  123. coll.hide_render = True
  124. for coll in collections:
  125. coll.hide_render = False
  126. bpy.ops.object.select_all(action='DESELECT')
  127. for obj in coll.objects:
  128. obj.select_set(True)
  129. data = {}
  130. data["name"] = coll.name
  131. data["views"] = []
  132. bound_min, bound_max = get_bounds(coll.objects)
  133. dimensions = bound_max - bound_min
  134. size = max(dimensions)
  135. global_bbox_center = 0.5 * (bound_min + bound_max)
  136. view_list = []
  137. lineart.grease_pencil_modifiers['Lineart'].source_collection = coll
  138. if "Views" in coll:
  139. for view in coll["Views"].split(","):
  140. view_list.append(VIEW_DATA[view])
  141. else:
  142. view_list = default_views
  143. for angles in view_list:
  144. c.location = global_bbox_center
  145. c.rotation_euler = Euler([angles[1] * pi / 2, 0, angles[0] * pi / 2])
  146. print(list(bound_min) + list(bound_max))
  147. _, c.data.ortho_scale = c.camera_fit_coords(bpy.context.evaluated_depsgraph_get(), list(bound_min) + list(bound_max))
  148. c.location = Vector([c.location[0], c.location[1], c.location[2]])
  149. c.data.ortho_scale *= 1.2
  150. rot = c.rotation_euler.to_matrix()
  151. rot.invert()
  152. c.location += Vector([0, 0, size * 2]) @ rot
  153. c.data.clip_start = size / 4
  154. c.data.clip_end = size * 8
  155. data["views"].append({
  156. "name": angles[3],
  157. "height": dimensions[angles[2]]
  158. })
  159. if "Volume" in coll:
  160. data["views"][-1]["volume"] = coll["Volume"]
  161. if "Mass" in coll:
  162. data["views"][-1]["mass"] = coll["Mass"]
  163. lineart.hide_render = False
  164. filename = f"{coll.name}-{angles[3]}.png"
  165. bpy.context.scene.render.filepath = workdir.joinpath(filename).resolve().__str__()
  166. bpy.ops.render.render(write_still = True)
  167. lineart.hide_render = True
  168. filename = f"{coll.name}-{angles[3]}-noline.png"
  169. bpy.context.scene.render.filepath = workdir.joinpath(filename).resolve().__str__()
  170. bpy.ops.render.render(write_still = True)
  171. all_data["forms"].append(data)
  172. coll.hide_render = True
  173. with open(workdir.joinpath("data.json"), "w") as file:
  174. json.dump(all_data, file)
  175. return {"FINISHED"}
  176. clses = [
  177. MVExport,
  178. MVConfigCollection
  179. ]