less copy protection, more size visualization
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

157 lines
5.4 KiB

  1. import bpy
  2. from mathutils import Vector, Euler
  3. from math import pi
  4. import json
  5. import os
  6. import bpy_extras
  7. import bmesh
  8. import time
  9. class ModalTimerOperator(bpy.types.Operator):
  10. """Operator which runs its self from a timer"""
  11. bl_idname = "wm.modal_timer_operator"
  12. bl_label = "Modal Timer Operator"
  13. _timer = None
  14. index = 0
  15. def menu_func(self, context):
  16. self.layout.operator(ModalTimerOperator.bl_idname)
  17. def modal(self, context, event):
  18. scene = context.scene
  19. if self.index >= len(self.sides):
  20. self.cancel(context)
  21. return {'CANCELLED'}
  22. if event.type == 'TIMER':
  23. print("HOLY COW!!!")
  24. self.capture(self.sides[self.index])
  25. self.index += 1
  26. if self.index < len(self.sides):
  27. self.position_camera(self.sides[self.index])
  28. else:
  29. self.export_data()
  30. return {'PASS_THROUGH'}
  31. def execute(self, context):
  32. print("execute!")
  33. self.c = bpy.data.objects["cam"]
  34. self.scene = bpy.context.scene
  35. selected = bpy.context.selected_objects[0]
  36. bpy.ops.object.mode_set(mode="OBJECT")
  37. bpy.ops.object.transform_apply( rotation = True )
  38. self.data = {}
  39. self.b = selected
  40. FRONT = [0, 1, 2, "Front"]
  41. SIDE = [1, 1, 2, "Side"]
  42. TOP = [0, 0, 1, "Top"]
  43. ANGLED = [0.5, 1, 2, "Angled"]
  44. self.sides = [FRONT, SIDE, TOP, ANGLED]
  45. path = "/tmp/macrovision/"
  46. media_path = "/home/crux/furry/macrovision/media/"
  47. media_folder = "buildings/Houses/"
  48. os.makedirs(path, exist_ok=True)
  49. os.makedirs(os.path.join(media_path, media_folder), exist_ok=True)
  50. wm = context.window_manager
  51. self._timer = wm.event_timer_add(0.25, window=context.window)
  52. wm.modal_handler_add(self)
  53. self.position_camera(self.sides[self.index])
  54. return {'RUNNING_MODAL'}
  55. def cancel(self, context):
  56. wm = context.window_manager
  57. wm.event_timer_remove(self._timer)
  58. def getView3dAreaAndRegion(self, context):
  59. for area in context.screen.areas:
  60. if area.type == "VIEW_3D":
  61. for region in area.regions:
  62. if region.type == "WINDOW":
  63. print("Found WINDOW")
  64. return area, region
  65. def select_border(self, context, view3dAreaAndRegion=None, extend=True):
  66. if not view3dAreaAndRegion:
  67. view3dAreaAndRegion = self.getView3dAreaAndRegion(context)
  68. print(view3dAreaAndRegion)
  69. view3dArea, view3dRegion = view3dAreaAndRegion
  70. override = context.copy()
  71. override['area'] = view3dArea
  72. override['region'] = view3dRegion
  73. bpy.ops.view3d.select_box(override,xmin=0,xmax=view3dArea.width,ymin=0,ymax=view3dArea.height,mode='SET')
  74. def position_camera(self, angles):
  75. bpy.ops.object.mode_set(mode="OBJECT")
  76. local_bbox_center = 0.125 * sum((Vector(box) for box in self.b.bound_box), Vector())
  77. global_bbox_center = self.b.matrix_world @ local_bbox_center
  78. self.c.data.ortho_scale = max(self.b.dimensions) * 1.3
  79. self.c.location = global_bbox_center
  80. self.c.rotation_euler = Euler([angles[1] * pi / 2, 0, angles[0] * pi / 2])
  81. rot = self.c.rotation_euler.to_matrix()
  82. rot.invert()
  83. self.c.location = self.c.location + Vector([0, 0, 100]) @ rot
  84. self.data[angles[3]] = self.b.dimensions[angles[2]]
  85. def capture(self, angles):
  86. bpy.ops.object.mode_set(mode="EDIT")
  87. bpy.ops.mesh.select_all(action="SELECT")
  88. bpy.ops.mesh.sort_elements(type='VIEW_ZAXIS', elements={'FACE'}, reverse=False)
  89. bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
  90. polygons = []
  91. edges = []
  92. self.select_border(bpy.context)
  93. bm.faces.ensure_lookup_table()
  94. bm.verts.ensure_lookup_table()
  95. bm.edges.ensure_lookup_table()
  96. for face in bm.faces:
  97. if not face.select:
  98. continue
  99. verts = []
  100. for vert in face.verts:
  101. co = bpy_extras.object_utils.world_to_camera_view(self.scene, self.c, vert.co)
  102. verts.append([co[0], co[1]])
  103. verts.append(verts[0])
  104. polygons.append({"verts": verts, "type": "bright" if face.material_index == 1 else "dark"})
  105. for edge in face.edges:
  106. if not edge.select:
  107. continue
  108. co1 = bpy_extras.object_utils.world_to_camera_view(self.scene, self.c, edge.verts[0].co)
  109. co2 = bpy_extras.object_utils.world_to_camera_view(self.scene, self.c, edge.verts[1].co)
  110. polygons.append({"type": "edge", "verts": [
  111. [co1[0], co1[1]],
  112. [co2[0], co2[1]]
  113. ]})
  114. bm.free()
  115. bpy.ops.object.mode_set(mode="OBJECT")
  116. with open(f"/tmp/polygons-{angles[3]}.json", "w", encoding="utf-8") as file:
  117. json.dump({"polygons": polygons}, file)
  118. def export_data(self):
  119. with open(f"/tmp/data-{self.b.name}.json", "w", encoding="utf-8") as file:
  120. json.dump({"name": self.b.name, "views": self.data}, file)
  121. def register():
  122. bpy.utils.register_class(ModalTimerOperator)
  123. bpy.types.VIEW3D_MT_object.append(ModalTimerOperator.menu_func)
  124. def unregister():
  125. bpy.utils.unregister_class(ModalTimerOperator)
  126. register()