463 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			463 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | # ##### BEGIN GPL LICENSE BLOCK ##### | ||
|  | # | ||
|  | #  This program is free software; you can redistribute it and/or | ||
|  | #  modify it under the terms of the GNU General Public License | ||
|  | #  as published by the Free Software Foundation; either version 2 | ||
|  | #  of the License, or (at your option) any later version. | ||
|  | # | ||
|  | #  This program is distributed in the hope that it will be useful, | ||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|  | #  GNU General Public License for more details. | ||
|  | # | ||
|  | #  You should have received a copy of the GNU General Public License | ||
|  | #  along with this program; if not, write to the Free Software Foundation, | ||
|  | #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
|  | # | ||
|  | # ##### END GPL LICENSE BLOCK ##### | ||
|  | 
 | ||
|  | import bpy | ||
|  | import threading | ||
|  | import numpy as np | ||
|  | import multiprocessing | ||
|  | from multiprocessing import Process, Pool | ||
|  | from mathutils import Vector | ||
|  | try: from .numba_functions import numba_lerp2 | ||
|  | except: pass | ||
|  | 
 | ||
|  | weight = [] | ||
|  | n_threads = multiprocessing.cpu_count() | ||
|  | 
 | ||
|  | class ThreadVertexGroup(threading.Thread): | ||
|  |     def __init__ ( self, id, vertex_group, n_verts): | ||
|  |         self.id = id | ||
|  |         self.vertex_group = vertex_group | ||
|  |         self.n_verts = n_verts | ||
|  |         threading.Thread.__init__ ( self ) | ||
|  | 
 | ||
|  |     def run (self): | ||
|  |         global weight | ||
|  |         global n_threads | ||
|  |         verts = np.arange(int(self.n_verts/8))*8 + self.id | ||
|  |         for v in verts: | ||
|  |             try: | ||
|  |                 weight[v] = self.vertex_group.weight(v) | ||
|  |             except: | ||
|  |                 pass | ||
|  | 
 | ||
|  | def thread_read_weight(_weight, vertex_group): | ||
|  |     global weight | ||
|  |     global n_threads | ||
|  |     print(n_threads) | ||
|  |     weight = _weight | ||
|  |     n_verts = len(weight) | ||
|  |     threads = [ThreadVertexGroup(i, vertex_group, n_verts) for i in range(n_threads)] | ||
|  |     for t in threads: t.start() | ||
|  |     for t in threads: t.join() | ||
|  |     return weight | ||
|  | 
 | ||
|  | def process_read_weight(id, vertex_group, n_verts): | ||
|  |     global weight | ||
|  |     global n_threads | ||
|  |     verts = np.arange(int(self.n_verts/8))*8 + self.id | ||
|  |     for v in verts: | ||
|  |         try: | ||
|  |             weight[v] = self.vertex_group.weight(v) | ||
|  |         except: | ||
|  |             pass | ||
|  | 
 | ||
|  | 
 | ||
|  | def read_weight(_weight, vertex_group): | ||
|  |     global weight | ||
|  |     global n_threads | ||
|  |     print(n_threads) | ||
|  |     weight = _weight | ||
|  |     n_verts = len(weight) | ||
|  |     n_cores = multiprocessing.cpu_count() | ||
|  |     pool = Pool(processes=n_cores) | ||
|  |     multiple_results = [pool.apply_async(process_read_weight, (i, vertex_group, n_verts)) for i in range(n_cores)] | ||
|  |     #processes = [Process(target=process_read_weight, args=(i, vertex_group, n_verts)) for i in range(n_threads)] | ||
|  |     #for t in processes: t.start() | ||
|  |     #for t in processes: t.join() | ||
|  |     return weight | ||
|  | 
 | ||
|  | #Recursivly transverse layer_collection for a particular name | ||
|  | def recurLayerCollection(layerColl, collName): | ||
|  |     found = None | ||
|  |     if (layerColl.name == collName): | ||
|  |         return layerColl | ||
|  |     for layer in layerColl.children: | ||
|  |         found = recurLayerCollection(layer, collName) | ||
|  |         if found: | ||
|  |             return found | ||
|  | 
 | ||
|  | def auto_layer_collection(): | ||
|  |     # automatically change active layer collection | ||
|  |     layer = bpy.context.view_layer.active_layer_collection | ||
|  |     layer_collection = bpy.context.view_layer.layer_collection | ||
|  |     if layer.hide_viewport or layer.collection.hide_viewport: | ||
|  |         collections = bpy.context.object.users_collection | ||
|  |         for c in collections: | ||
|  |             lc = recurLayerCollection(layer_collection, c.name) | ||
|  |             if not c.hide_viewport and not lc.hide_viewport: | ||
|  |                 bpy.context.view_layer.active_layer_collection = lc | ||
|  | 
 | ||
|  | def lerp(a, b, t): | ||
|  |     return a + (b - a) * t | ||
|  | 
 | ||
|  | def _lerp2(v1, v2, v3, v4, v): | ||
|  |     v12 = v1.lerp(v2,v.x) # + (v2 - v1) * v.x | ||
|  |     v34 = v3.lerp(v4,v.x) # + (v4 - v3) * v.x | ||
|  |     return v12.lerp(v34, v.y)# + (v34 - v12) * v.y | ||
|  | 
 | ||
|  | def lerp2(v1, v2, v3, v4, v): | ||
|  |     v12 = v1 + (v2 - v1) * v.x | ||
|  |     v34 = v3 + (v4 - v3) * v.x | ||
|  |     return v12 + (v34 - v12) * v.y | ||
|  | 
 | ||
|  | def lerp3(v1, v2, v3, v4, v): | ||
|  |     loc = lerp2(v1.co, v2.co, v3.co, v4.co, v) | ||
|  |     nor = lerp2(v1.normal, v2.normal, v3.normal, v4.normal, v) | ||
|  |     nor.normalize() | ||
|  |     return loc + nor * v.z | ||
|  | 
 | ||
|  | def np_lerp2(v00, v10, v01, v11, vx, vy): | ||
|  |     #try: | ||
|  |     #    co2 = numba_lerp2(v00, v10, v01, v11, vx, vy) | ||
|  |     #except: | ||
|  |     co0 = v00 + (v10 - v00) * vx | ||
|  |     co1 = v01 + (v11 - v01) * vx | ||
|  |     co2 = co0 + (co1 - co0) * vy | ||
|  |     return co2 | ||
|  | 
 | ||
|  | 
 | ||
|  | # Prevent Blender Crashes with handlers | ||
|  | def set_animatable_fix_handler(self, context): | ||
|  |     old_handlers = [] | ||
|  |     blender_handlers = bpy.app.handlers.render_init | ||
|  |     for h in blender_handlers: | ||
|  |         if "turn_off_animatable" in str(h): | ||
|  |             old_handlers.append(h) | ||
|  |     for h in old_handlers: blender_handlers.remove(h) | ||
|  |     ################ blender_handlers.append(turn_off_animatable) | ||
|  |     return | ||
|  | 
 | ||
|  | def turn_off_animatable(scene): | ||
|  |     for o in bpy.data.objects: | ||
|  |         o.tissue_tessellate.bool_run = False | ||
|  |         o.reaction_diffusion_settings.run = False | ||
|  |         #except: pass | ||
|  |     return | ||
|  | 
 | ||
|  | ### OBJECTS ### | ||
|  | 
 | ||
|  | def convert_object_to_mesh(ob, apply_modifiers=True, preserve_status=True): | ||
|  |     try: ob.name | ||
|  |     except: return None | ||
|  |     if ob.type != 'MESH': | ||
|  |         if not apply_modifiers: | ||
|  |             mod_visibility = [m.show_viewport for m in ob.modifiers] | ||
|  |             for m in ob.modifiers: m.show_viewport = False | ||
|  |         #ob.modifiers.update() | ||
|  |         #dg = bpy.context.evaluated_depsgraph_get() | ||
|  |         #ob_eval = ob.evaluated_get(dg) | ||
|  |         #me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg) | ||
|  |         me = simple_to_mesh(ob) | ||
|  |         new_ob = bpy.data.objects.new(ob.data.name, me) | ||
|  |         new_ob.location, new_ob.matrix_world = ob.location, ob.matrix_world | ||
|  |         if not apply_modifiers: | ||
|  |             for m,vis in zip(ob.modifiers,mod_visibility): m.show_viewport = vis | ||
|  |     else: | ||
|  |         if apply_modifiers: | ||
|  |             new_ob = ob.copy() | ||
|  |             new_me = simple_to_mesh(ob) | ||
|  |             new_ob.modifiers.clear() | ||
|  |             new_ob.data = new_me | ||
|  |         else: | ||
|  |             new_ob = ob.copy() | ||
|  |             new_ob.data = ob.data.copy() | ||
|  |             new_ob.modifiers.clear() | ||
|  |     bpy.context.collection.objects.link(new_ob) | ||
|  |     if preserve_status: | ||
|  |         new_ob.select_set(False) | ||
|  |     else: | ||
|  |         for o in bpy.context.view_layer.objects: o.select_set(False) | ||
|  |         new_ob.select_set(True) | ||
|  |         bpy.context.view_layer.objects.active = new_ob | ||
|  |     return new_ob | ||
|  | 
 | ||
|  | def simple_to_mesh(ob): | ||
|  |     dg = bpy.context.evaluated_depsgraph_get() | ||
|  |     ob_eval = ob.evaluated_get(dg) | ||
|  |     me = bpy.data.meshes.new_from_object(ob_eval, preserve_all_data_layers=True, depsgraph=dg) | ||
|  |     me.calc_normals() | ||
|  |     return me | ||
|  | 
 | ||
|  | def join_objects(objects, link_to_scene=True, make_active=False): | ||
|  |     C = bpy.context | ||
|  |     bm = bmesh.new() | ||
|  | 
 | ||
|  |     materials = {} | ||
|  |     faces_materials = [] | ||
|  |     dg = C.evaluated_depsgraph_get() | ||
|  |     for o in objects: | ||
|  |         bm.from_object(o, dg) | ||
|  |         # add object's material to the dictionary | ||
|  |         for m in o.data.materials: | ||
|  |             if m not in materials: materials[m] = len(materials) | ||
|  |         for f in o.data.polygons: | ||
|  |             index = f.material_index | ||
|  |             mat = o.material_slots[index].material | ||
|  |             new_index = materials[mat] | ||
|  |             faces_materials.append(new_index) | ||
|  |     bm.verts.ensure_lookup_table() | ||
|  |     bm.edges.ensure_lookup_table() | ||
|  |     bm.faces.ensure_lookup_table() | ||
|  |     # assign new indexes | ||
|  |     for index, f in zip(faces_materials, bm.faces): f.material_index = index | ||
|  |     # create object | ||
|  |     me = bpy.data.meshes.new('joined') | ||
|  |     bm.to_mesh(me) | ||
|  |     me.update() | ||
|  |     ob = bpy.data.objects.new('joined', me) | ||
|  |     if link_to_scene: C.collection.objects.link(ob) | ||
|  |     # make active | ||
|  |     if make_active: | ||
|  |         for o in C.view_layer.objects: o.select_set(False) | ||
|  |         ob.select_set(True) | ||
|  |         C.view_layer.objects.active = ob | ||
|  |     # add materials | ||
|  |     for m in materials.keys(): ob.data.materials.append(m) | ||
|  |     return ob | ||
|  | 
 | ||
|  | ### MESH FUNCTIONS | ||
|  | 
 | ||
|  | def get_vertices_numpy(mesh): | ||
|  |     n_verts = len(mesh.vertices) | ||
|  |     verts = [0]*n_verts*3 | ||
|  |     mesh.vertices.foreach_get('co', verts) | ||
|  |     verts = np.array(verts).reshape((n_verts,3)) | ||
|  |     return verts | ||
|  | 
 | ||
|  | def get_vertices_and_normals_numpy(mesh): | ||
|  |     n_verts = len(mesh.vertices) | ||
|  |     verts = [0]*n_verts*3 | ||
|  |     normals = [0]*n_verts*3 | ||
|  |     mesh.vertices.foreach_get('co', verts) | ||
|  |     mesh.vertices.foreach_get('normal', normals) | ||
|  |     verts = np.array(verts).reshape((n_verts,3)) | ||
|  |     normals = np.array(normals).reshape((n_verts,3)) | ||
|  |     return verts, normals | ||
|  | 
 | ||
|  | def get_edges_numpy(mesh): | ||
|  |     n_edges = len(mesh.edges) | ||
|  |     edges = [0]*n_edges*2 | ||
|  |     mesh.edges.foreach_get('vertices', edges) | ||
|  |     edges = np.array(edges).reshape((n_edges,2)).astype('int') | ||
|  |     return edges | ||
|  | 
 | ||
|  | def get_edges_id_numpy(mesh): | ||
|  |     n_edges = len(mesh.edges) | ||
|  |     edges = [0]*n_edges*2 | ||
|  |     mesh.edges.foreach_get('vertices', edges) | ||
|  |     edges = np.array(edges).reshape((n_edges,2)) | ||
|  |     indexes = np.arange(n_edges).reshape((n_edges,1)) | ||
|  |     edges = np.concatenate((edges,indexes), axis=1) | ||
|  |     return edges | ||
|  | 
 | ||
|  | def get_vertices(mesh): | ||
|  |     n_verts = len(mesh.vertices) | ||
|  |     verts = [0]*n_verts*3 | ||
|  |     mesh.vertices.foreach_get('co', verts) | ||
|  |     verts = np.array(verts).reshape((n_verts,3)) | ||
|  |     verts = [Vector(v) for v in verts] | ||
|  |     return verts | ||
|  | 
 | ||
|  | def get_faces(mesh): | ||
|  |     faces = [[v for v in f.vertices] for f in mesh.polygons] | ||
|  |     return faces | ||
|  | 
 | ||
|  | def get_faces_numpy(mesh): | ||
|  |     faces = [[v for v in f.vertices] for f in mesh.polygons] | ||
|  |     return np.array(faces) | ||
|  | 
 | ||
|  | def get_faces_edges_numpy(mesh): | ||
|  |     faces = [v.edge_keys for f in mesh.polygons] | ||
|  |     return np.array(faces) | ||
|  | 
 | ||
|  | #try: | ||
|  | #from numba import jit, njit | ||
|  | #from numba.typed import List | ||
|  | ''' | ||
|  | @jit | ||
|  | def find_curves(edges, n_verts): | ||
|  |     #verts_dict = {key:[] for key in range(n_verts)} | ||
|  |     verts_dict = {} | ||
|  |     for key in range(n_verts): verts_dict[key] = [] | ||
|  |     for e in edges: | ||
|  |         verts_dict[e[0]].append(e[1]) | ||
|  |         verts_dict[e[1]].append(e[0]) | ||
|  |     curves = []#List() | ||
|  |     loop1 = True | ||
|  |     while loop1: | ||
|  |         if len(verts_dict) == 0: | ||
|  |             loop1 = False | ||
|  |             continue | ||
|  |         # next starting point | ||
|  |         v = list(verts_dict.keys())[0] | ||
|  |         # neighbors | ||
|  |         v01 = verts_dict[v] | ||
|  |         if len(v01) == 0: | ||
|  |             verts_dict.pop(v) | ||
|  |             continue | ||
|  |         curve = []#List() | ||
|  |         curve.append(v)         # add starting point | ||
|  |         curve.append(v01[0])    # add neighbors | ||
|  |         verts_dict.pop(v) | ||
|  |         loop2 = True | ||
|  |         while loop2: | ||
|  |             last_point = curve[-1] | ||
|  |             #if last_point not in verts_dict: break | ||
|  |             v01 = verts_dict[last_point] | ||
|  |             # curve end | ||
|  |             if len(v01) == 1: | ||
|  |                 verts_dict.pop(last_point) | ||
|  |                 loop2 = False | ||
|  |                 continue | ||
|  |             if v01[0] == curve[-2]: | ||
|  |                 curve.append(v01[1]) | ||
|  |                 verts_dict.pop(last_point) | ||
|  |             elif v01[1] == curve[-2]: | ||
|  |                 curve.append(v01[0]) | ||
|  |                 verts_dict.pop(last_point) | ||
|  |             else: | ||
|  |                 loop2 = False | ||
|  |                 continue | ||
|  |             if curve[0] == curve[-1]: | ||
|  |                 loop2 = False | ||
|  |                 continue | ||
|  |         curves.append(curve) | ||
|  |     return curves | ||
|  | ''' | ||
|  | def find_curves(edges, n_verts): | ||
|  |     verts_dict = {key:[] for key in range(n_verts)} | ||
|  |     for e in edges: | ||
|  |         verts_dict[e[0]].append(e[1]) | ||
|  |         verts_dict[e[1]].append(e[0]) | ||
|  |     curves = [] | ||
|  |     while True: | ||
|  |         if len(verts_dict) == 0: break | ||
|  |         # next starting point | ||
|  |         v = list(verts_dict.keys())[0] | ||
|  |         # neighbors | ||
|  |         v01 = verts_dict[v] | ||
|  |         if len(v01) == 0: | ||
|  |             verts_dict.pop(v) | ||
|  |             continue | ||
|  |         curve = [] | ||
|  |         if len(v01) > 1: curve.append(v01[1])    # add neighbors | ||
|  |         curve.append(v)         # add starting point | ||
|  |         curve.append(v01[0])    # add neighbors | ||
|  |         verts_dict.pop(v) | ||
|  |         # start building curve | ||
|  |         while True: | ||
|  |             #last_point = curve[-1] | ||
|  |             #if last_point not in verts_dict: break | ||
|  | 
 | ||
|  |             # try to change direction if needed | ||
|  |             if curve[-1] in verts_dict: pass | ||
|  |             elif curve[0] in verts_dict: curve.reverse() | ||
|  |             else: break | ||
|  | 
 | ||
|  |             # neighbors points | ||
|  |             last_point = curve[-1] | ||
|  |             v01 = verts_dict[last_point] | ||
|  | 
 | ||
|  |             # curve end | ||
|  |             if len(v01) == 1: | ||
|  |                 verts_dict.pop(last_point) | ||
|  |                 if curve[0] in verts_dict: continue | ||
|  |                 else: break | ||
|  | 
 | ||
|  |             # chose next point | ||
|  |             new_point = None | ||
|  |             if v01[0] == curve[-2]: new_point = v01[1] | ||
|  |             elif v01[1] == curve[-2]: new_point = v01[0] | ||
|  |             #else: break | ||
|  | 
 | ||
|  |             #if new_point != curve[1]: | ||
|  |             curve.append(new_point) | ||
|  |             verts_dict.pop(last_point) | ||
|  |             if curve[0] == curve[-1]: | ||
|  |                 verts_dict.pop(new_point) | ||
|  |                 break | ||
|  |         curves.append(curve) | ||
|  |     return curves | ||
|  | 
 | ||
|  | def curve_from_points(points, name='Curve'): | ||
|  |     curve = bpy.data.curves.new(name,'CURVE') | ||
|  |     for c in points: | ||
|  |         s = curve.splines.new('POLY') | ||
|  |         s.points.add(len(c)) | ||
|  |         for i,p in enumerate(c): s.points[i].co = p.xyz + [1] | ||
|  |     ob_curve = bpy.data.objects.new(name,curve) | ||
|  |     return ob_curve | ||
|  | 
 | ||
|  | def curve_from_pydata(points, indexes, name='Curve', skip_open=False, merge_distance=1, set_active=True): | ||
|  |     curve = bpy.data.curves.new(name,'CURVE') | ||
|  |     curve.dimensions = '3D' | ||
|  |     for c in indexes: | ||
|  |         # cleanup | ||
|  |         pts = np.array([points[i] for i in c]) | ||
|  |         if merge_distance > 0: | ||
|  |             pts1 = np.roll(pts,1,axis=0) | ||
|  |             dist = np.linalg.norm(pts1-pts, axis=1) | ||
|  |             count = 0 | ||
|  |             n = len(dist) | ||
|  |             mask = np.ones(n).astype('bool') | ||
|  |             for i in range(n): | ||
|  |                 count += dist[i] | ||
|  |                 if count > merge_distance: count = 0 | ||
|  |                 else: mask[i] = False | ||
|  |             pts = pts[mask] | ||
|  | 
 | ||
|  |         bool_cyclic = c[0] == c[-1] | ||
|  |         if skip_open and not bool_cyclic: continue | ||
|  |         s = curve.splines.new('POLY') | ||
|  |         n_pts = len(pts) | ||
|  |         s.points.add(n_pts-1) | ||
|  |         w = np.ones(n_pts).reshape((n_pts,1)) | ||
|  |         co = np.concatenate((pts,w),axis=1).reshape((n_pts*4)) | ||
|  |         s.points.foreach_set('co',co) | ||
|  |         s.use_cyclic_u = bool_cyclic | ||
|  |     ob_curve = bpy.data.objects.new(name,curve) | ||
|  |     bpy.context.collection.objects.link(ob_curve) | ||
|  |     if set_active: | ||
|  |         bpy.context.view_layer.objects.active = ob_curve | ||
|  |     return ob_curve | ||
|  | 
 | ||
|  | def curve_from_vertices(indexes, verts, name='Curve'): | ||
|  |     curve = bpy.data.curves.new(name,'CURVE') | ||
|  |     for c in indexes: | ||
|  |         s = curve.splines.new('POLY') | ||
|  |         s.points.add(len(c)) | ||
|  |         for i,p in enumerate(c): s.points[i].co = verts[p].co.xyz + [1] | ||
|  |     ob_curve = bpy.data.objects.new(name,curve) | ||
|  |     return ob_curve | ||
|  | 
 | ||
|  | ### WEIGHT FUNCTIONS ### | ||
|  | 
 | ||
|  | def get_weight(vertex_group, n_verts): | ||
|  |     weight = [0]*n_verts | ||
|  |     for i in range(n_verts): | ||
|  |         try: weight[i] = vertex_group.weight(i) | ||
|  |         except: pass | ||
|  |     return weight | ||
|  | 
 | ||
|  | def get_weight_numpy(vertex_group, n_verts): | ||
|  |     weight = [0]*n_verts | ||
|  |     for i in range(n_verts): | ||
|  |         try: weight[i] = vertex_group.weight(i) | ||
|  |         except: pass | ||
|  |     return np.array(weight) |