Advertisement
AnomalousUnderdog

Blender Script: Show Triangle Count

May 20th, 2012
1,187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.84 KB | None | 0 0
  1. import bpy
  2. import blf
  3. import bgl
  4.  
  5. #
  6. # show how many triangles your 3d model would have
  7. # if its faces were all converted to triangles
  8. # this is mostly useful for videogame models
  9. # where polygon budget is quantified in triangles
  10. # since game engines convert everything to triangles anyway
  11. #
  12. # Limitations:
  13. #
  14. # 1. Only works properly on one 3D View for now. Multiple 3D Views don't display triangle count properly
  15. # 2. Doesn't update while a mesh is being edited. Only updates once the user goes back to object mode.
  16. # 3. Triangle count of selection only works on objects as a whole. Doesn't count per selected faces while in edit mode.
  17. #
  18.  
  19. bl_info = {
  20.     "name": "Polygon Count Display",
  21.     "author": "Anomalous Underdog",
  22.     "version": (0, 0, 1),
  23.     "blender": (2, 6, 3),
  24.     "location": "View3D > Properties panel > Poly Count Display",
  25.     "description": "Shows how many triangles your 3d model would have if its faces were all converted to triangles.",
  26.     "warning": "",
  27.     "wiki_url": "",
  28.     "tracker_url": "",
  29.     "support": 'TESTING',
  30.     "category": "3D View"
  31. }
  32.  
  33. class PolyCountPanel(bpy.types.Panel):
  34.     bl_label = "Poly Count Display"
  35.     bl_idname = "OBJECT_PT_poly_count"
  36.     bl_space_type = "VIEW_3D"
  37.     bl_region_type = "UI"
  38.    
  39.     def draw(self, context):
  40.         sc = context.scene
  41.         wm = context.window_manager
  42.         layout = self.layout
  43.  
  44.         if not wm.polycount_run:
  45.             layout.operator("view3d.polycount", text="Start display",
  46.                 icon='PLAY')
  47.         else:
  48.             layout.operator("view3d.polycount", text="Stop display",
  49.                 icon='PAUSE')
  50.                
  51.         col = layout.column(align=True)
  52.         #row = col.row(align=True)
  53.         #row.prop(sc, "polycount_show_in_3d_view")
  54.         row = col.row(align=True)
  55.         row.prop(sc, "polycount_pos_x")
  56.         row.prop(sc, "polycount_pos_y")
  57.         row = col.row(align=True)
  58.         row.prop(sc, "polycount_font_size")
  59.  
  60.         layout.prop(sc, "polycount_font_color")
  61.    
  62.  
  63. # properties used by the script
  64. def init_properties():
  65.     scene = bpy.types.Scene
  66.     #view3d = bpy.types.SpaceView3D
  67.     wm = bpy.types.WindowManager
  68.  
  69.     scene.polycount_pos_x = bpy.props.IntProperty(
  70.         name="Pos X",
  71.         description="Margin on the x axis",
  72.         default=23,
  73.         min=0,
  74.         max=100)
  75.     scene.polycount_pos_y = bpy.props.IntProperty(
  76.         name="Pos Y",
  77.         description="Margin on the y axis",
  78.         default=20,
  79.         min=0,
  80.         max=100)
  81.     scene.polycount_font_size = bpy.props.IntProperty(
  82.         name="Font",
  83.         description="Fontsize",
  84.         default=15, min=10, max=150)
  85.     scene.polycount_font_color = bpy.props.FloatVectorProperty(
  86.         name="Color",
  87.         description="Font color",
  88.         default=(1.0, 1.0, 1.0),
  89.         min=0,
  90.         max=1,
  91.         subtype='COLOR')
  92.    
  93.     #view3d._polycount_show_in_3d_view = bpy.props.BoolProperty(
  94.     #    name="Show poly count in 3d View",
  95.     #    description = "Shows poly count in 3d view if true.",
  96.     #    default = False)
  97.     #view3d.polycount_show_in_3d_view = property(showget, showset)
  98.    
  99.     wm.polycount_run = bpy.props.BoolProperty(default=False)
  100.        
  101. # removal of properties when script is disabled
  102. def clear_properties():
  103.     props = ["polycount_run", "polycount_pos_x", "polycount_pos_y",
  104.      "polycount_font_size", "polycount_font_color",
  105.      "polycount_show_in_3d_view"]
  106.     for p in props:
  107.         if bpy.context.window_manager.get(p) != None:
  108.             del bpy.context.window_manager[p]
  109.         try:
  110.             x = getattr(bpy.types.WindowManager, p)
  111.             del x
  112.         except:
  113.             pass
  114.  
  115.  
  116. class PolyCountOperator(bpy.types.Operator):
  117.     bl_idname = "view3d.polycount"
  118.     bl_label = "Polygon Count Display Tool"
  119.     bl_description = "Display polygon count in the 3D-view"
  120.  
  121.     _handle = None
  122.     #_timer = None
  123.    
  124.     visible_triangle_count = dict()
  125.     selection_triangle_count = dict()
  126.     #show = dict()
  127.  
  128.     def modal(self, context, event):
  129.         if context.area:
  130.             context.area.tag_redraw()
  131.        
  132.         self.update_polycount(context)
  133.        
  134.         if not context.window_manager.polycount_run:
  135.             # stop script
  136.             view3dId = get_space_id(context.space_data)
  137.             context.region.callback_remove(self._handle)
  138.             return {'CANCELLED'}
  139.  
  140.         return {'PASS_THROUGH'}
  141.            
  142.     def cancel(self, context):
  143.         if context.window_manager.polycount_run:
  144.             context.region.callback_remove(self._handle)
  145.             context.window_manager.polycount_run = False
  146.         return {'CANCELLED'}
  147.  
  148.     #@classmethod
  149.     #def poll(cls, context):
  150.     #    print("poll")
  151.     #    
  152.     #    cls.update_polycount(context)
  153.     #    
  154.     #    return context.active_object is not None
  155.  
  156.     def update_polycount(self, context):
  157.         select_tris = 0
  158.         visible_tris = 0
  159.        
  160.         for object in context.selected_objects:
  161.             if (object.type == 'MESH'):
  162.                 select_tris += get_triangle_count(object)
  163.            
  164.         for object in context.visible_objects:
  165.             if (object.type == 'MESH'):
  166.                 visible_tris += get_triangle_count(object)
  167.        
  168.         sc = context.scene
  169.        
  170.         view3dId = get_space_id(context.space_data)
  171.        
  172.         PolyCountOperator.visible_triangle_count[view3dId] = visible_tris
  173.         PolyCountOperator.selection_triangle_count[view3dId] = select_tris
  174.  
  175.  
  176.     #def execute(self, context):
  177.     #    print("execute")
  178.     #    return {'FINISHED'}
  179.  
  180.     def invoke(self, context, event):
  181.         #print("invoke")
  182.        
  183.         if context.area.type == 'VIEW_3D':
  184.             if context.window_manager.polycount_run == False:
  185.                 # operator is called for the first time, start everything
  186.                 print("initialized")
  187.                 context.window_manager.polycount_run = True
  188.                 context.window_manager.modal_handler_add(self)
  189.                
  190.                 self._handle = context.region.callback_add(draw_callback_px,
  191.                     (self, context), 'POST_PIXEL')
  192.                
  193.                 return {'RUNNING_MODAL'}
  194.             else:
  195.                 # operator is called again, stop displaying
  196.                 context.window_manager.polycount_run = False
  197.                 print("stopped")
  198.                 return {'CANCELLED'}
  199.         else:
  200.             self.report({'WARNING'}, "View3D not found, can't run operator")
  201.             return {'CANCELLED'}
  202.  
  203. def get_display_location(context):
  204.     scene = context.scene
  205.  
  206.     width = context.region.width
  207.     height = context.region.height
  208.  
  209.     pos_x = scene.polycount_pos_x
  210.     pos_y = height - scene.polycount_pos_y
  211.  
  212.     return(pos_x, pos_y)
  213.  
  214. def draw_callback_px(self, context):
  215.     wm = context.window_manager
  216.     sc = context.scene
  217.    
  218.     if not wm.polycount_run:
  219.         return
  220.  
  221.     font_size  = sc.polycount_font_size
  222.     pos_x, pos_y = get_display_location(context)
  223.  
  224.     # draw text in the 3d-view
  225.     # ========================
  226.     blf.size(0, sc.polycount_font_size, 72)
  227.     r, g, b = sc.polycount_font_color
  228.     bgl.glColor3f(r, g, b)
  229.    
  230.     view3dId = get_space_id(context.space_data)
  231.    
  232.     visible_tri_count = PolyCountOperator.visible_triangle_count.get(view3dId, -1)
  233.    
  234.     if visible_tri_count > -1:
  235.         text = "All: " + format(visible_tri_count, ',d') + " triangles"
  236.         text1height = blf.dimensions(0, text)[1]
  237.  
  238.         blf.position(0, pos_x, pos_y - text1height, 0)
  239.         blf.draw(0, text)
  240.    
  241.     selection_tri_count = PolyCountOperator.selection_triangle_count.get(view3dId, -1);
  242.    
  243.     if selection_tri_count > 0:
  244.         text = "Selection: " + format(selection_tri_count, ',d') + " triangles"
  245.         text2height = blf.dimensions(0, text)[1]
  246.  
  247.         blf.position(0, pos_x, pos_y - text1height - text2height - 5, 0)
  248.         blf.draw(0, text)
  249.  
  250. #
  251. # a quad would convert to 2 triangles
  252. # a 5-sided polygon would convert to 3 triangles
  253. # a hexagon would convert to 4 triangles
  254. # etc.
  255. #
  256. # so formula is:
  257. #  triangle count = number of corners > 2 ? number of corners - 2 : 0
  258. #
  259.  
  260. def get_triangle_count(object):
  261.     triangle_count = 0
  262.     decimateIdx = -1
  263.  
  264.     for modifierIdx, modifier in enumerate(list(object.modifiers)):
  265.         if modifier.type == 'DECIMATE' and modifier.show_viewport:
  266.             triangle_count = modifier.face_count
  267.             decimateIdx = modifierIdx
  268.            
  269.     if decimateIdx == -1:
  270.         for p in object.data.polygons:
  271.             count = p.loop_total
  272.             if count > 2:
  273.                 triangle_count += count - 2
  274.  
  275.     for modifierIdx, modifier in enumerate(list(object.modifiers)):
  276.         if modifier.type == 'MIRROR' and modifier.show_viewport and modifierIdx > decimateIdx:
  277.             if modifier.use_x:
  278.                 triangle_count *= 2
  279.             if modifier.use_y:
  280.                 triangle_count *= 2
  281.             if modifier.use_z:
  282.                 triangle_count *= 2
  283.            
  284.     return triangle_count
  285.  
  286. def get_vertex_count(object):
  287.     return len(object.data.vertices)
  288.  
  289.  
  290. classes = [PolyCountOperator, PolyCountPanel]
  291.  
  292.  
  293. def register():
  294.     init_properties()
  295.     for c in classes:
  296.         bpy.utils.register_class(c)
  297.  
  298.  
  299. def unregister():
  300.     for c in classes:
  301.         bpy.utils.unregister_class(c)
  302.     clear_properties()
  303.  
  304.  
  305. # Screen -> Area -> Space
  306. def get_space_id(object):
  307.     screenList = list(bpy.data.screens)
  308.     for screenIdx, screen in enumerate(screenList):
  309.         for areaIdx, area in enumerate(list(screen.areas)):
  310.             for spaceIdx, space in enumerate(list(area.spaces)):
  311.                 if (space == object):
  312.                     return str(screenIdx) + "." + str(areaIdx) + "." + str(spaceIdx)
  313.  
  314.  
  315. if __name__ == "__main__":
  316.     register()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement