[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [3413] contrib/py/scripts/addons/ space_view3d_enhanced_3d_cursor.py: Use new MeshCache class; added condition to not raycast objects without polygons

dima glib dima.glib at gmail.com
Mon May 28 15:06:27 CEST 2012


Revision: 3413
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=3413
Author:   dairin0d
Date:     2012-05-28 13:06:26 +0000 (Mon, 28 May 2012)
Log Message:
-----------
Use new MeshCache class; added condition to not raycast objects without polygons

Modified Paths:
--------------
    contrib/py/scripts/addons/space_view3d_enhanced_3d_cursor.py

Modified: contrib/py/scripts/addons/space_view3d_enhanced_3d_cursor.py
===================================================================
--- contrib/py/scripts/addons/space_view3d_enhanced_3d_cursor.py	2012-05-28 05:51:04 UTC (rev 3412)
+++ contrib/py/scripts/addons/space_view3d_enhanced_3d_cursor.py	2012-05-28 13:06:26 UTC (rev 3413)
@@ -2477,7 +2477,8 @@
     def __init__(self, scene, shade):
         SnapUtilityBase.__init__(self)
         
-        self.cache = MeshCache(scene)
+        convert_types = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'}
+        self.cache = MeshCache(scene, convert_types)
         
         # ? seems that dict is enough
         self.bbox_cache = {}#collections.OrderedDict()
@@ -2489,13 +2490,10 @@
         mesh.update(calc_tessface=True)
         #mesh.calc_tessface()
         
-        self.bbox_obj = self.cache.create_temporary_mesh_obj(mesh, Matrix())
+        self.bbox_obj = self.cache._make_obj(mesh, None)
         self.bbox_obj.hide = True
         self.bbox_obj.draw_type = 'WIRE'
         self.bbox_obj.name = "BoundBoxSnap"
-        # make it displayable
-        #self.cache.scene.objects.link(self.bbox_obj)
-        #self.cache.scene.update()
         
         self.shade_bbox = (shade == 'BOUNDBOX')
     
@@ -2522,7 +2520,7 @@
         bpy.data.objects.remove(self.bbox_obj)
         bpy.data.meshes.remove(mesh)
         
-        self.cache.dispose()
+        self.cache.clear()
     
     def hide_bbox(self, hide):
         if self.bbox_obj.hide == hide:
@@ -2556,7 +2554,8 @@
             m_combined = sys_matrix_inv * m
             bbox = [None, None]
             
-            mesh_obj = self.cache[obj, True, self.editmode]
+            variant = ('RAW' if self.editmode else 'PREVIEW')
+            mesh_obj = self.cache.get(obj, variant, reuse=False)
             if (mesh_obj is None) or self.shade_bbox or \
                     (obj.draw_type == 'BOUNDS'):
                 if is_local:
@@ -2644,9 +2643,10 @@
             
             if not is_bbox:
                 # Ensure we work with raycastable object.
-                obj = self.cache[obj, force, edit]
-                if obj is None:
-                    continue # the object has no geometry
+                variant = ('RAW' if edit else 'PREVIEW')
+                obj = self.cache.get(obj, variant, reuse=(not force))
+                if (obj is None) or (not obj.data.polygons):
+                    continue # the object has no raycastable geometry
             
             # If ray must be infinite, ensure that
             # endpoints are outside of bounding volume
@@ -2887,125 +2887,276 @@
         return n
 
 # ====== CONVERTED-TO-MESH OBJECTS CACHE ====== #
-class MeshCache:
-    # ====== INITIALIZATION / CLEANUP ====== #
-    def __init__(self, scene):
-        self.scene = scene
+#============================================================================#
+class ToggleObjectMode:
+    def __init__(self, mode='OBJECT'):
+        if not isinstance(mode, str):
+            mode = ('OBJECT' if mode else None)
         
-        self.mesh_cache = {}
-        self.object_cache = {}
-        self.edit_object = None
+        self.mode = mode
     
-    def dispose(self):
-        if self.edit_object:
-            mesh = self.edit_object.data
-            bpy.data.objects.remove(self.edit_object)
-            bpy.data.meshes.remove(mesh)
-        del self.edit_object
+    def __enter__(self):
+        if self.mode:
+            edit_preferences = bpy.context.user_preferences.edit
+            
+            self.global_undo = edit_preferences.use_global_undo
+            self.prev_mode = bpy.context.object.mode
+            
+            if self.prev_mode != self.mode:
+                edit_preferences.use_global_undo = False
+                bpy.ops.object.mode_set(mode=self.mode)
         
-        for rco in self.object_cache.values():
-            if rco:
-                bpy.data.objects.remove(rco)
-        del self.object_cache
+        return self
     
-        for mesh in self.mesh_cache.values():
-            bpy.data.meshes.remove(mesh)
-        del self.mesh_cache
+    def __exit__(self, type, value, traceback):
+        if self.mode:
+            edit_preferences = bpy.context.user_preferences.edit
+            
+            if self.prev_mode != self.mode:
+                bpy.ops.object.mode_set(mode=self.prev_mode)
+                edit_preferences.use_global_undo = self.global_undo
+
+class MeshCacheItem:
+    def __init__(self):
+        self.variants = {}
     
-    # ====== GET RELEVANT MESH/OBJECT ====== #
-    def __convert(self, obj, force=False, apply_modifiers=True, \
-                  add_to_cache=True):
-        # In Edit (and Sculpt?) mode mesh will not reflect
-        # changes until mode is changed to Object.
-        unstable_shape = ('EDIT' in obj.mode) or ('SCULPT' in obj.mode)
+    def __getitem__(self, variant):
+        return self.variants[variant][0]
+    
+    def __setitem__(self, variant, conversion):
+        mesh = conversion[0].data
+        #mesh.update(calc_tessface=True)
+        #mesh.calc_tessface()
+        mesh.calc_normals()
         
-        force = force or (len(obj.modifiers) != 0)
+        self.variants[variant] = conversion
+    
+    def __contains__(self, variant):
+        return variant in self.variants
+    
+    def dispose(self):
+        for obj, converted in self.variants.values():
+            if converted:
+                mesh = obj.data
+                bpy.data.objects.remove(obj)
+                bpy.data.meshes.remove(mesh)
+        self.variants = None
+
+class MeshCache:
+    """
+    Keeps a cache of mesh equivalents of requested objects.
+    It is assumed that object's data does not change while
+    the cache is in use.
+    """
+    
+    variants_enum = {'RAW', 'PREVIEW', 'RENDER'}
+    variants_normalization = {
+        'MESH':{},
+        'CURVE':{},
+        'SURFACE':{},
+        'FONT':{},
+        'META':{'RAW':'PREVIEW'},
+        'ARMATURE':{'RAW':'PREVIEW', 'RENDER':'PREVIEW'},
+        'LATTICE':{'RAW':'PREVIEW', 'RENDER':'PREVIEW'},
+        'EMPTY':{'RAW':'PREVIEW', 'RENDER':'PREVIEW'},
+        'CAMERA':{'RAW':'PREVIEW', 'RENDER':'PREVIEW'},
+        'LAMP':{'RAW':'PREVIEW', 'RENDER':'PREVIEW'},
+        'SPEAKER':{'RAW':'PREVIEW', 'RENDER':'PREVIEW'},
+    }
+    conversible_types = {'MESH', 'CURVE', 'SURFACE', 'FONT',
+                         'META', 'ARMATURE', 'LATTICE'}
+    convert_types = conversible_types
+    
+    def __init__(self, scene, convert_types=None):
+        self.scene = scene
+        if convert_types:
+            self.convert_types = convert_types
+        self.cached = {}
+    
+    def __del__(self):
+        self.clear()
+    
+    def clear(self, expect_zero_users=False):
+        for cache_item in self.cached.values():
+            if cache_item:
+                try:
+                    cache_item.dispose()
+                except RuntimeError:
+                    if expect_zero_users:
+                        raise
+        self.cached.clear()
+    
+    def __delitem__(self, obj):
+        cache_item = self.cached.pop(obj, None)
+        if cache_item:
+            cache_item.dispose()
+    
+    def __contains__(self, obj):
+        return obj in self.cached
+    
+    def __getitem__(self, obj):
+        if isinstance(obj, tuple):
+            return self.get(*obj)
+        return self.get(obj)
+    
+    def get(self, obj, variant='PREVIEW', reuse=True):
+        if variant not in self.variants_enum:
+            raise ValueError("Mesh variant must be one of %s" %
+                             self.variants_enum)
         
-        if (obj.data.bl_rna.name == 'Mesh'):
-            if not (force or unstable_shape):
-                # Existing mesh actually satisfies us
-                return obj
+        # Make sure the variant is proper for this type of object
+        variant = (self.variants_normalization[obj.type].
+                   get(variant, variant))
         
-        #if (not force) and (obj.data in self.mesh_cache):
-        if obj.data in self.mesh_cache:
-            mesh = self.mesh_cache[obj.data]
+        if obj in self.cached:
+            cache_item = self.cached[obj]
+            try:
+                # cache_item is None if object isn't conversible to mesh
+                return (None if (cache_item is None)
+                        else cache_item[variant])
+            except KeyError:
+                pass
         else:
-            if unstable_shape:
-                prev_mode = obj.mode
-                bpy.ops.object.mode_set(mode='OBJECT')
-            
-            mesh = obj.to_mesh(self.scene, apply_modifiers, 'PREVIEW')
-            mesh.name = tmp_name
-            
-            if unstable_shape:
-                bpy.ops.object.mode_set(mode=prev_mode)
-            
-            if add_to_cache:
-                self.mesh_cache[obj.data] = mesh
+            cache_item = None
         
-        rco = self.create_temporary_mesh_obj(mesh, obj.matrix_world)
-        rco.show_x_ray = obj.show_x_ray # necessary for corrent bbox display
+        if obj.type not in self.conversible_types:
+            self.cached[obj] = None
+            return None
         
-        return rco
+        if not cache_item:
+            cache_item = MeshCacheItem()
+            self.cached[obj] = cache_item
+        
+        conversion = self._convert(obj, variant, reuse)
+        cache_item[variant] = conversion
+        
+        return conversion[0]
     
-    def __getitem__(self, args):
-        # If more than one argument is passed to getitem,
-        # Python wraps them into tuple.
-        if not isinstance(args, tuple):
-            args = (args,)
+    def _convert(self, obj, variant, reuse=True):
+        obj_type = obj.type
+        obj_mode = obj.mode
+        data = obj.data
         
-        obj = args[0]
-        force = (args[1] if len(args) > 1 else True)
-        edit = (args[2] if len(args) > 2 else False)
+        if obj_type == 'MESH':
+            if reuse and ((variant == 'RAW') or (len(obj.modifiers) == 0)):
+                return (obj, False)
+            else:
+                force_objectmode = (obj_mode in ('EDIT', 'SCULPT'))
+                return (self._to_mesh(obj, variant, force_objectmode), True)
+        elif obj_type in ('CURVE', 'SURFACE', 'FONT'):
+            if variant == 'RAW':
+                bm = bmesh.new()

@@ Diff output truncated at 10240 characters. @@


More information about the Bf-extensions-cvs mailing list