shelves folder init
parent
10afe009fb
commit
515c363c4a
|
@ -1,103 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon': 'RECOVER_LAST',
|
|
||||||
'description': 'Duplicate current low as mid',
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
# make a proxy through python
|
|
||||||
|
|
||||||
### / unused, just because it's interesting (but relinking break hide statement)
|
|
||||||
## https://blender.stackexchange.com/questions/157562/sorting-collections-alphabetically-in-the-outliner
|
|
||||||
def sort_collection(collection, case = False):
|
|
||||||
|
|
||||||
if collection.children is None: return
|
|
||||||
|
|
||||||
children = sorted (
|
|
||||||
collection.children,
|
|
||||||
key = lambda c: c.name if case else c.name.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
for child in children:
|
|
||||||
collection.children.unlink(child)
|
|
||||||
collection.children.link(child)
|
|
||||||
sort_collection(child)
|
|
||||||
|
|
||||||
def sort_all_collection():
|
|
||||||
# case_sensitive sort, (default is False)
|
|
||||||
case_sensitive = True
|
|
||||||
|
|
||||||
for scene in bpy.data.scenes:
|
|
||||||
sort_collection(scene.collection, case_sensitive)
|
|
||||||
### unused /
|
|
||||||
|
|
||||||
|
|
||||||
def set_collection(ob, collection, unlink=True) :
|
|
||||||
''' link an object in a collection and create it if necessary, if unlink object is removed from other collections'''
|
|
||||||
scn = bpy.context.scene
|
|
||||||
col = None
|
|
||||||
visible = False
|
|
||||||
linked = False
|
|
||||||
|
|
||||||
# check if collection exist or create it
|
|
||||||
for c in bpy.data.collections :
|
|
||||||
if c.name == collection : col = c
|
|
||||||
if not col : col = bpy.data.collections.new(name=collection)
|
|
||||||
|
|
||||||
# link the collection to the scene's collection if necessary
|
|
||||||
for c in scn.collection.children :
|
|
||||||
if c.name == col.name : visible = True
|
|
||||||
if not visible : scn.collection.children.link(col)
|
|
||||||
|
|
||||||
# check if the object is already in the collection and link it if necessary
|
|
||||||
for o in col.objects :
|
|
||||||
if o == ob : linked = True
|
|
||||||
if not linked : col.objects.link(ob)
|
|
||||||
|
|
||||||
# remove object from scene's collection
|
|
||||||
for o in scn.collection.objects :
|
|
||||||
if o == ob : scn.collection.objects.unlink(ob)
|
|
||||||
|
|
||||||
# if unlink flag we remove the object from other collections
|
|
||||||
if unlink :
|
|
||||||
for c in ob.users_collection :
|
|
||||||
if c.name != collection : c.objects.unlink(ob)
|
|
||||||
return col
|
|
||||||
|
|
||||||
|
|
||||||
# mid = None
|
|
||||||
# low = None
|
|
||||||
# for c in bpy.data.collections:
|
|
||||||
# if re.search(r'vetement.*_mid', c.name, re.I):
|
|
||||||
# mid = c
|
|
||||||
# if re.search(r'vetement.*_low', c.name, re.I):
|
|
||||||
# low = c
|
|
||||||
|
|
||||||
def backup_low_to_mid():
|
|
||||||
low = mid = None
|
|
||||||
for o in bpy.context.scene.objects:
|
|
||||||
if re.search(r'vetements?_mid', o.name, re.I):
|
|
||||||
mid = o
|
|
||||||
if re.search(r'vetements?_low', o.name, re.I):
|
|
||||||
low = o
|
|
||||||
|
|
||||||
if not low:
|
|
||||||
print('ERROR', 'low not found')
|
|
||||||
return
|
|
||||||
|
|
||||||
if not mid and low:
|
|
||||||
#create mid by duplicating low
|
|
||||||
mid = low.copy()
|
|
||||||
mid.name = low.name.replace('_low', '_mid')
|
|
||||||
mid.data = low.data.copy()# also copy data !
|
|
||||||
mid.data.name = low.data.name.replace('_low', '_mid')
|
|
||||||
col = set_collection(mid, mid.name)
|
|
||||||
# hide viewport
|
|
||||||
col.hide_viewport = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
print('ERROR', 'mid already exists')
|
|
||||||
return
|
|
||||||
|
|
||||||
backup_low_to_mid()
|
|
|
@ -1,27 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon': 'SYNTAX_OFF',
|
|
||||||
'description': 'rename all lowercase (a bit dangerous, check if any break)',
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
|
|
||||||
for c in bpy.data.collections:
|
|
||||||
new = c.name.lower()
|
|
||||||
if bpy.data.collections.get(new):
|
|
||||||
print(f'{new} exists!')
|
|
||||||
else:
|
|
||||||
c.name = new
|
|
||||||
|
|
||||||
for o in bpy.data.meshes:
|
|
||||||
new = o.name.lower()
|
|
||||||
if bpy.data.meshes.get(new):
|
|
||||||
print(f'{new} exists!')
|
|
||||||
else:
|
|
||||||
o.name = new
|
|
||||||
|
|
||||||
for o in bpy.data.objects:
|
|
||||||
new = o.name.lower()
|
|
||||||
if bpy.data.objects.get(new):
|
|
||||||
print(f'{new} exists!')
|
|
||||||
else:
|
|
||||||
o.name = new
|
|
|
@ -1,90 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon': 'MOD_CLOTH',
|
|
||||||
'description': 'Decimate cloth',
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from pathlib import Path
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def set_panel(panel):
|
|
||||||
'''take a panel name and apply it to properties zone'''
|
|
||||||
for area in bpy.context.screen.areas:
|
|
||||||
if area.type == 'PROPERTIES':
|
|
||||||
for space in area.spaces:
|
|
||||||
if space.type == 'PROPERTIES':
|
|
||||||
space.context = panel
|
|
||||||
return (1)
|
|
||||||
return (0)
|
|
||||||
|
|
||||||
|
|
||||||
def get_panel():
|
|
||||||
'''return active panel name of the properties zone'''
|
|
||||||
for area in bpy.context.screen.areas:
|
|
||||||
if area.type == 'PROPERTIES':
|
|
||||||
for space in area.spaces:
|
|
||||||
if space.type == 'PROPERTIES':
|
|
||||||
return(space.context)
|
|
||||||
return (0)
|
|
||||||
|
|
||||||
def get_override():
|
|
||||||
for window in bpy.context.window_manager.windows:
|
|
||||||
screen = window.screen
|
|
||||||
for area in screen.areas:
|
|
||||||
if area.type == 'PROPERTIES':
|
|
||||||
#for region in area.regions:
|
|
||||||
# if region.type == 'WINDOW':
|
|
||||||
return {'window': window, 'screen': screen, 'area': area}
|
|
||||||
|
|
||||||
|
|
||||||
def simplify_cloth(poly_target):
|
|
||||||
if not get_override():
|
|
||||||
print('ERROR', f'Need a properties windows editor open !')
|
|
||||||
return
|
|
||||||
|
|
||||||
obj = bpy.context.object
|
|
||||||
|
|
||||||
mods = obj.modifiers
|
|
||||||
|
|
||||||
polycount = len(obj.data.polygons)
|
|
||||||
|
|
||||||
if polycount < poly_target:
|
|
||||||
print('ERROR', f'Already lowpoly ({polycount} polygons)')
|
|
||||||
return
|
|
||||||
|
|
||||||
target_ratio = poly_target / polycount
|
|
||||||
print('target_ratio: ', target_ratio)
|
|
||||||
|
|
||||||
sdef = None
|
|
||||||
|
|
||||||
for m in mods:
|
|
||||||
if m.type == 'SURFACE_DEFORM':
|
|
||||||
sdef = m
|
|
||||||
break
|
|
||||||
|
|
||||||
if not sdef:
|
|
||||||
print('ERROR', 'no surface deform found')
|
|
||||||
return
|
|
||||||
|
|
||||||
set_panel('MODIFIER')
|
|
||||||
properties_override = get_override()
|
|
||||||
|
|
||||||
if sdef.is_bound:
|
|
||||||
bpy.ops.object.surfacedeform_bind(properties_override, modifier="SurfaceDeform")
|
|
||||||
|
|
||||||
# create decimate and move to top
|
|
||||||
decim = obj.modifiers.new('Decimate', 'DECIMATE')
|
|
||||||
bpy.ops.object.modifier_move_to_index(modifier="Decimate", index=0)
|
|
||||||
|
|
||||||
# use target ratio
|
|
||||||
decim.ratio = target_ratio
|
|
||||||
# apply
|
|
||||||
bpy.ops.object.modifier_apply(modifier="Decimate")
|
|
||||||
|
|
||||||
# rebind
|
|
||||||
bpy.ops.object.surfacedeform_bind(properties_override, modifier="SurfaceDeform")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
simplify_cloth(15000)
|
|
|
@ -1,275 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon' : 'PHYSICS',
|
|
||||||
'description' : 'Copy dynamic physics object from selected instance_collection',
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from os.path import abspath, relpath, dirname, basename, join
|
|
||||||
from time import time
|
|
||||||
C = bpy.context
|
|
||||||
D = bpy.data
|
|
||||||
scene = C.scene
|
|
||||||
|
|
||||||
def set_collection(ob, collection, unlink=True) :
|
|
||||||
''' link an object in a collection and create it if necessary, if unlink object is removed from other collections'''
|
|
||||||
scn = bpy.context.scene
|
|
||||||
col = None
|
|
||||||
visible = False
|
|
||||||
linked = False
|
|
||||||
|
|
||||||
# check if collection exist or create it
|
|
||||||
for c in bpy.data.collections :
|
|
||||||
if c.name == collection : col = c
|
|
||||||
if not col : col = bpy.data.collections.new(name=collection)
|
|
||||||
|
|
||||||
# link the collection to the scene's collection if necessary
|
|
||||||
for c in scn.collection.children :
|
|
||||||
if c.name == col.name : visible = True
|
|
||||||
if not visible : scn.collection.children.link(col)
|
|
||||||
|
|
||||||
# check if the object is already in the collection and link it if necessary
|
|
||||||
for o in col.objects :
|
|
||||||
if o == ob : linked = True
|
|
||||||
if not linked : col.objects.link(ob)
|
|
||||||
|
|
||||||
# remove object from scene's collection
|
|
||||||
for o in scn.collection.objects :
|
|
||||||
if o == ob : scn.collection.objects.unlink(ob)
|
|
||||||
|
|
||||||
# if unlink flag we remove the object from other collections
|
|
||||||
if unlink :
|
|
||||||
for c in ob.users_collection :
|
|
||||||
if c.name != collection : c.objects.unlink(ob)
|
|
||||||
|
|
||||||
|
|
||||||
def append_stuff(blendfile, section, obj):
|
|
||||||
filepath = blendfile
|
|
||||||
directory = join(blendfile, section)#join(blendfile, section)
|
|
||||||
filename = obj
|
|
||||||
print('Append')
|
|
||||||
print('filepath: ', filepath)
|
|
||||||
print('directory: ', directory)
|
|
||||||
print('filename: ', filename)
|
|
||||||
|
|
||||||
print(f'''using:
|
|
||||||
bpy.ops.wm.append(
|
|
||||||
filepath='{filepath}',
|
|
||||||
filename='{filename}',
|
|
||||||
directory='{directory}',
|
|
||||||
active_collection=True,
|
|
||||||
)
|
|
||||||
''')
|
|
||||||
|
|
||||||
bpy.ops.wm.append(
|
|
||||||
filepath=filepath,
|
|
||||||
filename=filename,
|
|
||||||
directory=directory,
|
|
||||||
filter_blender=True, filter_backup=False, filter_image=False, filter_movie=False,
|
|
||||||
filter_python=False, filter_font=False, filter_sound=False, filter_text=False,
|
|
||||||
filter_archive=False, filter_btx=False, filter_collada=False, filter_alembic=False,
|
|
||||||
filter_folder=True, filter_blenlib=True, filemode=1, display_type='DEFAULT', sort_method='FILE_SORT_ALPHA',
|
|
||||||
link=False, autoselect=True, active_collection=True,#default -> active_collection=True
|
|
||||||
instance_collections=False, set_fake=False,
|
|
||||||
use_recursive=True
|
|
||||||
)
|
|
||||||
#return {'finished'}
|
|
||||||
|
|
||||||
## data way (not working as is...)
|
|
||||||
'''
|
|
||||||
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
|
|
||||||
data_to.objects = [o for o in data_from.objects if o == obj]
|
|
||||||
|
|
||||||
#link object to current scene
|
|
||||||
for obj in data_to.objects:
|
|
||||||
if obj is not None:
|
|
||||||
C.scene.collection.objects.link(obj)
|
|
||||||
'''
|
|
||||||
|
|
||||||
def set_soft_body(ob, mod, bake=False):
|
|
||||||
if bake:
|
|
||||||
print(f'baking {ob.name} > {mod.name}')
|
|
||||||
ptc = mod.point_cache
|
|
||||||
|
|
||||||
name = ptc.name
|
|
||||||
override = {'scene': bpy.context.scene,
|
|
||||||
'active_object': ob,#no sure of this line.. .
|
|
||||||
'point_cache': ptc}
|
|
||||||
if bake:
|
|
||||||
if name:#kill point cache related to point cache name
|
|
||||||
# delete_point_cache(name)
|
|
||||||
bpy.ops.ptcache.free_bake(override)
|
|
||||||
## bpy.ops.ptcache.free_bake_all(override)
|
|
||||||
|
|
||||||
#ptc.is_outdated = True # read-only
|
|
||||||
|
|
||||||
ptc.frame_start = scene.frame_start
|
|
||||||
ptc.frame_end = scene.frame_end
|
|
||||||
# ptc.filepath = join(dirname(D.filepath), splitext(basename)[0]+'_cache')
|
|
||||||
|
|
||||||
if not ptc.use_disk_cache:
|
|
||||||
ptc.use_disk_cache = True
|
|
||||||
print('enabling use disk cache')
|
|
||||||
|
|
||||||
if bake:
|
|
||||||
print('>>> baking')
|
|
||||||
start = time()
|
|
||||||
#bpy.ops.ptcache.bake(override, bake=False)# bake to current frame
|
|
||||||
bpy.ops.ptcache.bake(override, bake=True)
|
|
||||||
print(f'Done {time()-start:.3f}')
|
|
||||||
|
|
||||||
def set_particle_system(ob, mod, bake=False):
|
|
||||||
if bake:
|
|
||||||
print(f'baking {ob.name} > {mod.name}')
|
|
||||||
ptc = mod.particle_system.point_cache
|
|
||||||
ptc.frame_start = scene.frame_start
|
|
||||||
ptc.frame_end = scene.frame_end
|
|
||||||
# ptc.filepath = join(dirname(D.filepath), splitext(basename)[0]+'_cache')
|
|
||||||
if not ptc.use_disk_cache:
|
|
||||||
ptc.use_disk_cache = True
|
|
||||||
print('enabling use disk cache')
|
|
||||||
|
|
||||||
if bake:
|
|
||||||
override = {'scene': bpy.context.scene,
|
|
||||||
'active_object': ob,#no sure of this line.. .
|
|
||||||
'point_cache': ptc}
|
|
||||||
print('>>> baking')
|
|
||||||
start = time()
|
|
||||||
#bpy.ops.ptcache.bake(override, bake=False)# bake to current frame
|
|
||||||
bpy.ops.ptcache.bake(override, bake=True)
|
|
||||||
print(f'Done {time()-start:.3f}')
|
|
||||||
|
|
||||||
def bake_mods(ob, bake=False):
|
|
||||||
for mod in ob.modifiers:
|
|
||||||
if mod.type == 'SOFT_BODY':
|
|
||||||
set_soft_body(ob, mod, bake)
|
|
||||||
|
|
||||||
if mod.type == 'PARTICLE_SYSTEM':
|
|
||||||
set_particle_system(ob, mod, bake)
|
|
||||||
|
|
||||||
|
|
||||||
def clear_obj_driver(ob):
|
|
||||||
'''delete visibility driver if any'''
|
|
||||||
print('clearing driver', ob.name)
|
|
||||||
ob.driver_remove('hide_viewport')
|
|
||||||
ob.driver_remove('hide_render')
|
|
||||||
ob.hide_viewport = False
|
|
||||||
ob.hide_render = False
|
|
||||||
|
|
||||||
def append_physics_obj(selected=True, purge=False, bake=False):
|
|
||||||
'''On selection or all scene'''
|
|
||||||
|
|
||||||
current_active_col = C.view_layer.active_layer_collection
|
|
||||||
|
|
||||||
if selected:
|
|
||||||
obpool = C.selected_objects
|
|
||||||
else:
|
|
||||||
obpool = bpy.context.scene.objects
|
|
||||||
|
|
||||||
pool = [o for o in obpool if o.type == 'EMPTY' and o.instance_collection]#PARTICLE_SYSTEM
|
|
||||||
if selected:
|
|
||||||
for proxyarm in [o for o in obpool if o.type == 'ARMATURE' and o.name.endswith('_proxy')]:
|
|
||||||
inst = proxyarm.proxy_collection
|
|
||||||
if inst not in pool: pool.append(inst)
|
|
||||||
|
|
||||||
linked = []
|
|
||||||
|
|
||||||
#[m for m in o.modifiers if m.type == 'SOFT_BODY']
|
|
||||||
for instance in pool:
|
|
||||||
print("instance", instance.name)#Dbg
|
|
||||||
for ob in instance.instance_collection.all_objects:
|
|
||||||
# print("ob", ob.name)#Dbg
|
|
||||||
if ob.type in ('MESH', 'CURVE'):
|
|
||||||
for mod in ob.modifiers:
|
|
||||||
if mod.type in ('SOFT_BODY', 'PARTICLE_SYSTEM'):
|
|
||||||
print(f'{ob.name} > {mod.name} ({mod.type})')
|
|
||||||
|
|
||||||
## check if object already exists in object base.
|
|
||||||
dynob_name = ob.name + '_DYN'
|
|
||||||
phycol = D.collections.get('Physics_sim')#Dynamic_physics
|
|
||||||
|
|
||||||
theob = None
|
|
||||||
if phycol: theob = phycol.objects.get(dynob_name)
|
|
||||||
|
|
||||||
if theob:
|
|
||||||
print(f'{dynob_name} already as object in scene')
|
|
||||||
continue
|
|
||||||
'''
|
|
||||||
if purge:#delete whats in physics collection, crashy as hell..
|
|
||||||
print(f'--> purge: deleting {dynob_name}')
|
|
||||||
D.objects.remove(theob)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
'''
|
|
||||||
|
|
||||||
## get library path
|
|
||||||
libpath = abspath(bpy.path.abspath(instance.instance_collection.library.filepath))
|
|
||||||
print('libpath: ', libpath)
|
|
||||||
#libpath = instance.instance_collection.library.filepath
|
|
||||||
#objpath = join(libpath, 'Object', ob.name)
|
|
||||||
|
|
||||||
if not phycol:
|
|
||||||
phycol = D.collections.new('Physics_sim')
|
|
||||||
C.scene.collection.children.link(phycol)
|
|
||||||
## make it active
|
|
||||||
C.view_layer.active_layer_collection = C.view_layer.layer_collection.children['Physics_sim']
|
|
||||||
|
|
||||||
## Append individual object, (physics object with a different name from the blend file, else it 'links' to the one withi instance_collection)
|
|
||||||
#append_stuff(libpath, 'Object', ob.name)
|
|
||||||
|
|
||||||
## test copy from existing object
|
|
||||||
newob = ob.copy()
|
|
||||||
#newob.make_local()
|
|
||||||
#newob.data.make_local()
|
|
||||||
set_collection(newob, 'Physics_sim')
|
|
||||||
linked.append(newob)
|
|
||||||
# newob.name = newob.name + '_DYN'
|
|
||||||
#clear_obj_driver(newob)##crash when clearing driver of the copied object immediately. Do it later
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
## get the newly appended object
|
|
||||||
newob = phycol.objects.get(ob.name)#not rename currently
|
|
||||||
if not newob:
|
|
||||||
print(f'problem, did not found "{dynob_name}" in physics collection')
|
|
||||||
continue
|
|
||||||
linked.append(newob)
|
|
||||||
'''
|
|
||||||
|
|
||||||
# setup proxy visibility...
|
|
||||||
if instance.name == 'jenny':
|
|
||||||
proxy = C.scene.objects.get('jenny_proxy')
|
|
||||||
hairdyn = proxy.pose.bones['root']['_RNA_UI'].get('hair_dynamic')
|
|
||||||
if hairdyn:
|
|
||||||
if hairdyn['max'] < 2:
|
|
||||||
#setmax
|
|
||||||
proxy.pose.bones['root']['_RNA_UI']['hair_dynamic']['max'] = 2
|
|
||||||
proxy.pose.bones['root']['_RNA_UI']['hair_dynamic']['soft_max'] = 2
|
|
||||||
#set val
|
|
||||||
proxy.pose.bones['root']['hair_dynamic'] = 2
|
|
||||||
|
|
||||||
if instance.name == 'boom':
|
|
||||||
proxy = C.scene.objects.get('boom_proxy')
|
|
||||||
hairdyn = proxy.pose.bones['root']['_RNA_UI'].get('hair_placeholder')
|
|
||||||
if hairdyn:
|
|
||||||
if hairdyn['max'] < 3:
|
|
||||||
#setmax
|
|
||||||
proxy.pose.bones['root']['_RNA_UI']['hair_placeholder']['max'] = 3
|
|
||||||
proxy.pose.bones['root']['_RNA_UI']['hair_placeholder']['soft_max'] = 3
|
|
||||||
#set val
|
|
||||||
proxy.pose.bones['root']['hair_placeholder'] = 3
|
|
||||||
|
|
||||||
for o in linked:#linked
|
|
||||||
print(f'-- linked: {o.name}')
|
|
||||||
# o.make_local()#localize then modify, usually no need to make local if appended
|
|
||||||
o.name = o.name + '_DYN'
|
|
||||||
clear_obj_driver(o)
|
|
||||||
print(o.name)
|
|
||||||
#set_collection(o, 'Physics_sim')
|
|
||||||
bake_mods(o, bake=False)
|
|
||||||
|
|
||||||
#reset active collection
|
|
||||||
C.view_layer.active_layer_collection = current_active_col
|
|
||||||
|
|
||||||
|
|
||||||
print()
|
|
||||||
append_physics_obj(selected=True, purge=False, bake=False)#purge bugged
|
|
|
@ -1,143 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon' : 'ACTION_TWEAK',
|
|
||||||
'description' : 'Bake action of selected objects',
|
|
||||||
'selection' : True
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from os.path import abspath, relpath, dirname, basename, join
|
|
||||||
from time import time
|
|
||||||
C = bpy.context
|
|
||||||
D = bpy.data
|
|
||||||
scene = C.scene
|
|
||||||
|
|
||||||
def set_collection(ob, collection, unlink=True) :
|
|
||||||
''' link an object in a collection and create it if necessary, if unlink object is removed from other collections'''
|
|
||||||
scn = bpy.context.scene
|
|
||||||
col = None
|
|
||||||
visible = False
|
|
||||||
linked = False
|
|
||||||
|
|
||||||
# check if collection exist or create it
|
|
||||||
for c in bpy.data.collections :
|
|
||||||
if c.name == collection : col = c
|
|
||||||
if not col : col = bpy.data.collections.new(name=collection)
|
|
||||||
|
|
||||||
# link the collection to the scene's collection if necessary
|
|
||||||
for c in scn.collection.children :
|
|
||||||
if c.name == col.name : visible = True
|
|
||||||
if not visible : scn.collection.children.link(col)
|
|
||||||
|
|
||||||
# check if the object is already in the collection and link it if necessary
|
|
||||||
for o in col.objects :
|
|
||||||
if o == ob : linked = True
|
|
||||||
if not linked : col.objects.link(ob)
|
|
||||||
|
|
||||||
# remove object from scene's collection
|
|
||||||
for o in scn.collection.objects :
|
|
||||||
if o == ob : scn.collection.objects.unlink(ob)
|
|
||||||
|
|
||||||
# if unlink flag we remove the object from other collections
|
|
||||||
if unlink :
|
|
||||||
for c in ob.users_collection :
|
|
||||||
if c.name != collection : c.objects.unlink(ob)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_soft_body(ob, mod, bake=False):
|
|
||||||
if bake:
|
|
||||||
print(f'baking {ob.name} > {mod.name}')
|
|
||||||
ptc = mod.point_cache
|
|
||||||
|
|
||||||
name = ptc.name
|
|
||||||
override = {'scene': bpy.context.scene,
|
|
||||||
'active_object': ob,#no sure of this line.. .
|
|
||||||
'point_cache': ptc}
|
|
||||||
if bake:
|
|
||||||
if name:#kill point cache related to point cache name
|
|
||||||
bpy.ops.ptcache.free_bake(override)
|
|
||||||
## bpy.ops.ptcache.free_bake_all(override)
|
|
||||||
|
|
||||||
#ptc.is_outdated = True # read-only
|
|
||||||
|
|
||||||
ptc.frame_start = scene.frame_start
|
|
||||||
ptc.frame_end = scene.frame_end
|
|
||||||
|
|
||||||
if not ptc.use_disk_cache:
|
|
||||||
ptc.use_disk_cache = True
|
|
||||||
print('enabling use disk cache')
|
|
||||||
|
|
||||||
if bake:
|
|
||||||
print('>>> baking')
|
|
||||||
start = time()
|
|
||||||
#bpy.ops.ptcache.bake(override, bake=False)# bake to current frame
|
|
||||||
bpy.ops.ptcache.bake(override, bake=True)
|
|
||||||
print(f'Done {time()-start:.3f}')
|
|
||||||
|
|
||||||
def set_particle_system(ob, mod, bake=False):
|
|
||||||
if bake:
|
|
||||||
print(f'baking {ob.name} > {mod.name}')
|
|
||||||
ptc = mod.particle_system.point_cache
|
|
||||||
ptc.frame_start = scene.frame_start
|
|
||||||
ptc.frame_end = scene.frame_end
|
|
||||||
# ptc.filepath = join(dirname(D.filepath), splitext(basename)[0]+'_cache')
|
|
||||||
if not ptc.use_disk_cache:
|
|
||||||
ptc.use_disk_cache = True
|
|
||||||
print('enabling use disk cache')
|
|
||||||
|
|
||||||
if bake:
|
|
||||||
override = {'scene': bpy.context.scene,
|
|
||||||
'active_object': ob,#no sure of this line.. .
|
|
||||||
'point_cache': ptc}
|
|
||||||
print('>>> baking')
|
|
||||||
start = time()
|
|
||||||
#bpy.ops.ptcache.bake(override, bake=False)# bake to current frame
|
|
||||||
bpy.ops.ptcache.bake(override, bake=True)
|
|
||||||
print(f'Done {time()-start:.3f}')
|
|
||||||
|
|
||||||
def bake_mods(ob, bake=False):
|
|
||||||
for mod in ob.modifiers:
|
|
||||||
if mod.type == 'SOFT_BODY':
|
|
||||||
set_soft_body(ob, mod, bake)
|
|
||||||
|
|
||||||
if mod.type == 'PARTICLE_SYSTEM':
|
|
||||||
set_particle_system(ob, mod, bake)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def bake_action(selected=True):
|
|
||||||
if selected:
|
|
||||||
pool = []
|
|
||||||
for o in C.selected_objects:
|
|
||||||
if o.animation_data.action:
|
|
||||||
print(f'{o.name} already has an action')
|
|
||||||
o.select_set(False)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
pool.append(o)
|
|
||||||
if not pool:
|
|
||||||
print('no object ready for action bake')
|
|
||||||
return
|
|
||||||
|
|
||||||
else:#go in physics ob collection
|
|
||||||
phycol = D.collections.get('Physics_sim')
|
|
||||||
if not phycol:
|
|
||||||
print('No physics collection')
|
|
||||||
return
|
|
||||||
|
|
||||||
pool = [o for o in phycol.all_objects if not o.hide_viewport and not o.animation_data.action]
|
|
||||||
#redefine selection
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
|
||||||
for o in pool:
|
|
||||||
o.select_set(True)
|
|
||||||
C.view_layer.objects.active = o
|
|
||||||
|
|
||||||
# get viewport override ? (no need if launched from custom shelf)
|
|
||||||
|
|
||||||
print('Baking action...')
|
|
||||||
# launch action baking
|
|
||||||
bpy.ops.nla.bake(frame_start=scene.frame_start, frame_end=scene.frame_end, only_selected=False, visual_keying=True, clear_constraints=True, clear_parents=True, bake_types={'OBJECT'})
|
|
||||||
|
|
||||||
|
|
||||||
bake_action(selected=info['selection'])
|
|
||||||
print('EOF')
|
|
|
@ -1,100 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon' : 'PHYSICS',
|
|
||||||
'description' : 'Bake modifier cache of selected objects',
|
|
||||||
'selection' : True
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from os.path import abspath, relpath, dirname, basename, join
|
|
||||||
from time import time
|
|
||||||
C = bpy.context
|
|
||||||
D = bpy.data
|
|
||||||
scene = C.scene
|
|
||||||
|
|
||||||
|
|
||||||
def set_soft_body(ob, mod, bake=False):
|
|
||||||
if bake:
|
|
||||||
print(f'baking {ob.name} > {mod.name}')
|
|
||||||
ptc = mod.point_cache
|
|
||||||
|
|
||||||
name = ptc.name
|
|
||||||
override = {'scene': bpy.context.scene,
|
|
||||||
'active_object': ob,#no sure of this line.. .
|
|
||||||
'point_cache': ptc}
|
|
||||||
if bake:
|
|
||||||
if name:#kill point cache related to point cache name
|
|
||||||
bpy.ops.ptcache.free_bake(override)
|
|
||||||
## bpy.ops.ptcache.free_bake_all(override)
|
|
||||||
|
|
||||||
#ptc.is_outdated = True # read-only
|
|
||||||
|
|
||||||
ptc.frame_start = scene.frame_start
|
|
||||||
ptc.frame_end = scene.frame_end
|
|
||||||
|
|
||||||
if not ptc.use_disk_cache:
|
|
||||||
ptc.use_disk_cache = True
|
|
||||||
print('enabling use disk cache')
|
|
||||||
|
|
||||||
if bake:
|
|
||||||
print('>>> baking')
|
|
||||||
start = time()
|
|
||||||
#bpy.ops.ptcache.bake(override, bake=False)# bake to current frame
|
|
||||||
bpy.ops.ptcache.bake(override, bake=True)
|
|
||||||
print(f'Done {time()-start:.3f}')
|
|
||||||
|
|
||||||
def set_particle_system(ob, mod, bake=False):
|
|
||||||
if bake:
|
|
||||||
print(f'baking {ob.name} > {mod.name}')
|
|
||||||
ptc = mod.particle_system.point_cache
|
|
||||||
ptc.frame_start = scene.frame_start
|
|
||||||
ptc.frame_end = scene.frame_end
|
|
||||||
# ptc.filepath = join(dirname(D.filepath), splitext(basename)[0]+'_cache')
|
|
||||||
if not ptc.use_disk_cache:
|
|
||||||
ptc.use_disk_cache = True
|
|
||||||
print('enabling use disk cache')
|
|
||||||
|
|
||||||
if bake:
|
|
||||||
override = {'scene': bpy.context.scene,
|
|
||||||
'active_object': ob,#no sure of this line.. .
|
|
||||||
'point_cache': ptc}
|
|
||||||
print('>>> baking')
|
|
||||||
start = time()
|
|
||||||
#bpy.ops.ptcache.bake(override, bake=False)# bake to current frame
|
|
||||||
bpy.ops.ptcache.bake(override, bake=True)
|
|
||||||
print(f'Done {time()-start:.3f}')
|
|
||||||
|
|
||||||
def bake_mods(ob, bake=False):
|
|
||||||
for mod in ob.modifiers:
|
|
||||||
if mod.type == 'SOFT_BODY':
|
|
||||||
set_soft_body(ob, mod, bake)
|
|
||||||
|
|
||||||
if mod.type == 'PARTICLE_SYSTEM':
|
|
||||||
set_particle_system(ob, mod, bake)
|
|
||||||
|
|
||||||
|
|
||||||
def bake_mod_cache(selected=True):
|
|
||||||
phycol = D.collections.get('Physics_sim')
|
|
||||||
if not phycol:
|
|
||||||
print('No physics collection')
|
|
||||||
return
|
|
||||||
|
|
||||||
if selected:
|
|
||||||
pool = []
|
|
||||||
for o in C.selected_objects:
|
|
||||||
if not phycol in o.users_collection:
|
|
||||||
print(f'{o.name} is not in Physics_sim collection' )
|
|
||||||
continue
|
|
||||||
pool.append(o)
|
|
||||||
|
|
||||||
if not pool:
|
|
||||||
print('no object ready for cache baking')
|
|
||||||
return
|
|
||||||
|
|
||||||
else:#go in physics ob collection
|
|
||||||
pool = [o for o in phycol.all_objects if not o.hide_viewport and not o.animation_data.action]
|
|
||||||
#automate preroll ?
|
|
||||||
for o in pool:
|
|
||||||
bake_mods(o, bake=True)
|
|
||||||
|
|
||||||
bake_mod_cache(selected=info['selection'])
|
|
||||||
print('EOF')
|
|
|
@ -1,19 +0,0 @@
|
||||||
info = {
|
|
||||||
'icon' : 'X',
|
|
||||||
'description' : 'Delete everything in collection Physics_sim',
|
|
||||||
}
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from os.path import abspath, relpath, dirname, basename, join
|
|
||||||
from time import time
|
|
||||||
C = bpy.context
|
|
||||||
D = bpy.data
|
|
||||||
scene = C.scene
|
|
||||||
|
|
||||||
|
|
||||||
phycol = D.collections.get('Physics_sim')#Dynamic_physics
|
|
||||||
if phycol:
|
|
||||||
for ob in phycol.all_objects:
|
|
||||||
D.objects.remove(ob)
|
|
||||||
|
|
||||||
D.collections.remove(phycol)
|
|
|
@ -1,144 +0,0 @@
|
||||||
import bpy
|
|
||||||
from mathutils import Vector, Matrix
|
|
||||||
from math import radians, degrees
|
|
||||||
from random import uniform
|
|
||||||
import bmesh
|
|
||||||
C = bpy.context
|
|
||||||
|
|
||||||
|
|
||||||
# Assumes we have a mesh object selected in OBJECT mode
|
|
||||||
|
|
||||||
def transfer_value(Value, OldMin, OldMax, NewMin, NewMax):
|
|
||||||
'''map a value from a range to another (transfer/translate value)'''
|
|
||||||
return (((Value - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
|
|
||||||
|
|
||||||
def chunks(lst, n):
|
|
||||||
"""Yield successive n-sized chunks from lst."""
|
|
||||||
for i in range(0, len(lst), n):
|
|
||||||
yield lst[i:i + n]
|
|
||||||
|
|
||||||
ob = C.object
|
|
||||||
M = ob.matrix_world
|
|
||||||
|
|
||||||
# Get the active mesh
|
|
||||||
me = ob.data
|
|
||||||
|
|
||||||
## get active group
|
|
||||||
#C.vertex_groups.active_index
|
|
||||||
|
|
||||||
if ob.type != 'MESH':
|
|
||||||
print('ERROR : not a mesh')
|
|
||||||
|
|
||||||
mode = bpy.context.mode
|
|
||||||
|
|
||||||
if mode == 'EDIT_MESH':
|
|
||||||
#me = bpy.context.edit_object.data
|
|
||||||
bm = bmesh.from_edit_mesh(me) #get Bmesh from edit
|
|
||||||
|
|
||||||
elif mode == 'OBJECT':
|
|
||||||
bm = bmesh.new() # create an empty BMesh
|
|
||||||
bm.from_mesh(me) # fill it in from a Mesh
|
|
||||||
|
|
||||||
bm.verts.ensure_lookup_table()
|
|
||||||
bm.edges.ensure_lookup_table()
|
|
||||||
bm.faces.ensure_lookup_table()
|
|
||||||
|
|
||||||
#deselect everything first
|
|
||||||
for f in bm.edges:f.select = False
|
|
||||||
for e in bm.edges:e.select = False
|
|
||||||
for v in bm.verts:v.select = False
|
|
||||||
|
|
||||||
|
|
||||||
# Modify the BMesh, can do anything here...
|
|
||||||
chunks = []
|
|
||||||
sc = 1
|
|
||||||
ec = 0
|
|
||||||
for v in bm.verts:
|
|
||||||
v.select_set(False)
|
|
||||||
#v.co.x += 1.0
|
|
||||||
if len(v.link_edges) == 1:#vertices chain tip
|
|
||||||
#sc+=1
|
|
||||||
#if sc == 2:
|
|
||||||
# sc = 0
|
|
||||||
chunks.append(v.index)
|
|
||||||
|
|
||||||
if len(chunks)%2 != 0:
|
|
||||||
print("list not pair")
|
|
||||||
|
|
||||||
for i in chunks:
|
|
||||||
bm.verts[i].select_set(True)
|
|
||||||
|
|
||||||
n = 2
|
|
||||||
pairs = [chunks[i:i + n] for i in range(0, len(chunks), n)]
|
|
||||||
#print("pairs", pairs)#Dbg
|
|
||||||
vlists = []
|
|
||||||
cursorloc = bpy.context.scene.cursor.location
|
|
||||||
for p in pairs:
|
|
||||||
vlen = p[1] - p[0]#number of vertices in chunk
|
|
||||||
#get_closest to 3d cursor
|
|
||||||
if (cursorloc - M @ bm.verts[p[0]].co).length < (cursorloc - M @ bm.verts[p[1]].co).length:
|
|
||||||
print(f'{p[0]} to {p[1]}')
|
|
||||||
bm.verts[p[1]].select = False
|
|
||||||
vlists.append( [i for i in range(p[0], p[1]+1)] )
|
|
||||||
|
|
||||||
else:# Last point is closer to cursor (so invert direction)
|
|
||||||
print(f'{p[1]} to {p[0]}')
|
|
||||||
bm.verts[p[0]].select = False
|
|
||||||
vlists.append( [i for i in reversed(range(p[0], p[1]+1))] )
|
|
||||||
|
|
||||||
|
|
||||||
if mode == 'EDIT_MESH':
|
|
||||||
bmesh.update_edit_mesh(me, True)
|
|
||||||
|
|
||||||
elif mode == 'OBJECT':
|
|
||||||
bm.to_mesh(me)
|
|
||||||
bm.free()
|
|
||||||
|
|
||||||
|
|
||||||
# tweak vertex group (need to be in object mode)
|
|
||||||
|
|
||||||
'''
|
|
||||||
## classic progressive
|
|
||||||
for vl in vlists:
|
|
||||||
vnum = len(vl)
|
|
||||||
for i, vid in enumerate(vl):
|
|
||||||
weight = i/vnum# progressive
|
|
||||||
weight = abs(weight-1)# reverse
|
|
||||||
weight = transfer_value(weight, 0, 1, 0.8, 1)# clamp between two value
|
|
||||||
C.object.vertex_groups.active.add([vid], weight, "REPLACE")
|
|
||||||
'''
|
|
||||||
|
|
||||||
## with a divider to limit
|
|
||||||
## ex with 5 points:
|
|
||||||
## divider 2 : 0, 0, 0 , 0.5, 1.0
|
|
||||||
## divider 3 : 0, 0, 0.33 , 0.66, 1.0
|
|
||||||
|
|
||||||
percentage = 0.4 #(0 for full progressive)
|
|
||||||
#divider = 3
|
|
||||||
for vl in vlists:
|
|
||||||
vnum = len(vl)
|
|
||||||
# limit = int(vnum/divider)
|
|
||||||
limit = int(vnum*percentage)
|
|
||||||
rest = vnum - limit
|
|
||||||
|
|
||||||
mini = uniform(0.86, 0.96)#random minimum
|
|
||||||
for i, vid in enumerate(vl):
|
|
||||||
#weight = i/vnum
|
|
||||||
w = weight = (i - limit)/(vnum-limit-1)
|
|
||||||
if weight < 0: weight=0
|
|
||||||
|
|
||||||
weight = abs(weight-1)#reverse
|
|
||||||
# mini = 0.9
|
|
||||||
weight = transfer_value(weight, 0, 1, mini, 1)# clamp between two last value
|
|
||||||
print(i, w, "->", weight)#Dbg
|
|
||||||
C.object.vertex_groups.active.add([vid], weight, "REPLACE")
|
|
||||||
|
|
||||||
print("vnum", vnum)#Dbg
|
|
||||||
print("random minimum", mini)#Dbg
|
|
||||||
print("limit", limit)#Dbg
|
|
||||||
print("rest", rest)#Dbg
|
|
||||||
print("full", rest + limit)#Dbg
|
|
||||||
|
|
||||||
|
|
||||||
#too smooth with 0.8 on fast movements
|
|
||||||
## transfer_value(weight, 0, 1, 0.8, 1)
|
|
|
@ -1,132 +0,0 @@
|
||||||
## create visibility on objects or armature selection from a bone of a rig ##
|
|
||||||
import bpy
|
|
||||||
|
|
||||||
def add_driver(source, target, prop, dataPath, index = -1, negative = False, func = ''):
|
|
||||||
''' Add driver to source prop (at index), driven by target dataPath '''
|
|
||||||
|
|
||||||
source.driver_remove(prop, index)
|
|
||||||
if index != -1:
|
|
||||||
d = source.driver_add( prop, index ).driver
|
|
||||||
else:
|
|
||||||
d = source.driver_add( prop ).driver
|
|
||||||
|
|
||||||
v = d.variables.new()
|
|
||||||
v.targets[0].id = target
|
|
||||||
v.targets[0].data_path = dataPath
|
|
||||||
|
|
||||||
d.expression = func + "(" + v.name + ")" if func else v.name
|
|
||||||
d.expression = d.expression if not negative else "-1 * " + d.expression
|
|
||||||
|
|
||||||
def create_hide_custom_prop(src_object, prop_name, prop_bone = ''):
|
|
||||||
'''
|
|
||||||
add source propertie with boolean option
|
|
||||||
place the hide prop on src_object with name prop_name
|
|
||||||
'''
|
|
||||||
|
|
||||||
rig = bpy.data.objects.get(src_object)
|
|
||||||
if not rig:
|
|
||||||
print(f"No objects named {src_object}")
|
|
||||||
return 1
|
|
||||||
if rig.type != 'ARMATURE':
|
|
||||||
print(f"Not an armature : {src_object}")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
#add target bone
|
|
||||||
if prop_bone:
|
|
||||||
holder = rig.pose.bones.get(prop_bone)
|
|
||||||
else:
|
|
||||||
holder = rig.pose.bones.get('root')
|
|
||||||
|
|
||||||
if not holder:
|
|
||||||
print(f'problem finding bone {prop_bone} (or root)')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# create
|
|
||||||
if not holder.get('_RNA_UI'):
|
|
||||||
holder['_RNA_UI'] = {}
|
|
||||||
|
|
||||||
if not prop_name in holder.keys() :
|
|
||||||
holder[prop_name] = 0
|
|
||||||
holder['_RNA_UI'][prop_name] = {"default": 0,"min":0,"max":1,"soft_min":0,"soft_max":1}
|
|
||||||
else:
|
|
||||||
print(f'{prop_name} : already exists on root key')
|
|
||||||
return
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def drive_selection_visibility(rig, prop_name, prop_bone = ''):
|
|
||||||
# add driver on selection
|
|
||||||
prefixs = ('MCH','DEF','ORG', 'WGT')
|
|
||||||
|
|
||||||
rig = bpy.data.objects.get(src_object)
|
|
||||||
if not rig:
|
|
||||||
print(f"No objects named {src_object}")
|
|
||||||
return 1
|
|
||||||
if rig.type != 'ARMATURE':
|
|
||||||
print(f"Not an armature : {src_object}")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
#add target bone
|
|
||||||
|
|
||||||
if not prop_bone:
|
|
||||||
prop_bone = 'root'
|
|
||||||
if not rig.pose.bones.get(prop_bone):
|
|
||||||
print(f'no bones {prop_bone} on rig {rig.name}')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
meshes = [i for i in bpy.context.selected_objects if i.type in ('MESH','CURVE','TEXT') and not i.name.startswith(('WGT', 'WDGT'))]
|
|
||||||
armatures = [i for i in bpy.context.selected_objects if i.type == 'ARMATURE']
|
|
||||||
|
|
||||||
if bpy.context.mode == 'POSE':
|
|
||||||
obarm = bpy.context.active_object
|
|
||||||
for bone in bpy.context.selected_pose_bones_from_active_object:
|
|
||||||
prop = 'bones["%s"].hide'%bone.name
|
|
||||||
index = -1
|
|
||||||
layer = bone.bone.layers
|
|
||||||
protect_layer = rig.data.layers_protected
|
|
||||||
### dont check for protected, strictly use selection.
|
|
||||||
# if bone.name.startswith(prefixs) or any([i==j==1 for i,j in zip(layer,protect_layer)]) :
|
|
||||||
# print(f'Skipped : Prefixed or protected bone : {bone.name}')
|
|
||||||
# rig.data.driver_remove(prop, index)
|
|
||||||
# continue
|
|
||||||
print(f'New : Driver on bone {bone.name}')
|
|
||||||
add_driver(obarm.data, rig, prop, f'pose.bones["{prop_bone}"]["{prop_name}"]', index)
|
|
||||||
return
|
|
||||||
|
|
||||||
for ob in meshes :
|
|
||||||
print('Object : ', obarm.name)
|
|
||||||
|
|
||||||
add_driver(ob, rig, 'hide_viewport', f'pose.bones["{prop_bone}"]["{prop_name}"]', -1)
|
|
||||||
add_driver(ob, rig, 'hide_render', f'pose.bones["{prop_bone}"]["{prop_name}"]', -1)
|
|
||||||
|
|
||||||
for obarm in armatures:
|
|
||||||
print('Armature : ', obarm.name)
|
|
||||||
## mask armature object
|
|
||||||
## add_driver(obarm, rig, 'hide_viewport', f'pose.bones["{prop_bone}"]["{prop_name}"]', -1)
|
|
||||||
## bette mask pose bones since its a proxy...
|
|
||||||
for bone in obarm.pose.bones :
|
|
||||||
prop = 'bones["%s"].hide'%bone.name
|
|
||||||
index = -1
|
|
||||||
layer = bone.bone.layers
|
|
||||||
protect_layer = rig.data.layers_protected
|
|
||||||
if bone.name.startswith(prefixs) or any([i==j==1 for i,j in zip(layer,protect_layer)]) :
|
|
||||||
print(f'Skipped : Prefixed or protected bone : {bone.name}')
|
|
||||||
rig.data.driver_remove(prop, index)
|
|
||||||
else :
|
|
||||||
print(f'New : Driver on bone {bone.name}')
|
|
||||||
add_driver(obarm.data, rig, prop, f'pose.bones["{prop_bone}"]["{prop_name}"]', index)
|
|
||||||
|
|
||||||
### ----
|
|
||||||
|
|
||||||
## write the name of the rig source (will put the propertie on the root of this armature)
|
|
||||||
prop_rig = 'name_of_the_rig'
|
|
||||||
|
|
||||||
## write the name of the propertie to attach
|
|
||||||
prop_name = "hide_something"#'hide_headband'
|
|
||||||
|
|
||||||
## prop_bone (bone holding the propertie), 'root' if left string empty.
|
|
||||||
prop_bone = ''
|
|
||||||
|
|
||||||
|
|
||||||
create_hide_custom_prop(prop_rig, prop_name, prop_bone = prop_bone)
|
|
||||||
drive_selection_visibility(prop_rig, prop_name, prop_bone = prop_bone)
|
|
|
@ -1,32 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
import bpy
|
|
||||||
import bmesh
|
|
||||||
|
|
||||||
ob = bpy.context.object
|
|
||||||
me = ob.data
|
|
||||||
|
|
||||||
bm = bmesh.new() # create an empty BMesh
|
|
||||||
bm.from_mesh(me) # fill it in from a Mesh
|
|
||||||
|
|
||||||
bm.verts.ensure_lookup_table()
|
|
||||||
bm.edges.ensure_lookup_table()
|
|
||||||
bm.faces.ensure_lookup_table()
|
|
||||||
|
|
||||||
#deselect everything first
|
|
||||||
for f in bm.edges:f.select = False
|
|
||||||
for e in bm.edges:e.select = False
|
|
||||||
for v in bm.verts:v.select = False
|
|
||||||
|
|
||||||
#Checker deselect on each separate mesh portion
|
|
||||||
ct = 0
|
|
||||||
for v in bm.verts:
|
|
||||||
ct+=1
|
|
||||||
if len(v.link_edges) == 1:#star/end of chain
|
|
||||||
# v.select_set(False)#already deselected
|
|
||||||
ct = 0#reset ct for next
|
|
||||||
else:
|
|
||||||
print(v.index, 'select', ct%2)
|
|
||||||
v.select_set(ct%2)
|
|
||||||
|
|
||||||
bm.to_mesh(me)
|
|
||||||
bm.free()
|
|
|
@ -1,286 +0,0 @@
|
||||||
bl_info = {
|
|
||||||
"name": "FTP sync",
|
|
||||||
"author": "Christophe Seux",
|
|
||||||
"version": (1, 0),
|
|
||||||
"blender": (2, 81, 0),
|
|
||||||
"warning": "",
|
|
||||||
"wiki_url": "",
|
|
||||||
"category": "User",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from bpy.types import Operator, AddonPreferences
|
|
||||||
from bpy.props import FloatVectorProperty
|
|
||||||
import os
|
|
||||||
from os.path import *
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
### EXCLUSIONS
|
|
||||||
EXCLUSIONS = ['.*','*.db','*.blend1','*~','*sync-conflict*','*.DS_Store']
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
TO DO
|
|
||||||
Accepts empty folders, option for type of sync (override etc)
|
|
||||||
'''
|
|
||||||
|
|
||||||
class Preferences(AddonPreferences):
|
|
||||||
bl_idname = __name__
|
|
||||||
|
|
||||||
domain : bpy.props.StringProperty(name="FTP Domain")
|
|
||||||
login : bpy.props.StringProperty(name="FTP Login")
|
|
||||||
password : bpy.props.StringProperty(name="FTP Password",subtype='PASSWORD')
|
|
||||||
ftp_folder : bpy.props.StringProperty(name="FTP folder", default = '/')
|
|
||||||
local_folder : bpy.props.StringProperty(name="Local Folder", subtype='DIR_PATH')
|
|
||||||
active_connection : bpy.props.BoolProperty(name="Active Connection", default = True)
|
|
||||||
sync_type : bpy.props.EnumProperty(
|
|
||||||
name="Sync Type",
|
|
||||||
items = [(i,i.title(),"") for i in ("ONLY_NEW", "OVERRIDE", "MORE_RECENT")],
|
|
||||||
default = "ONLY_NEW")
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
layout.prop(self, "domain")
|
|
||||||
layout.prop(self, "login")
|
|
||||||
layout.prop(self, "password")
|
|
||||||
layout.prop(self, "ftp_folder")
|
|
||||||
layout.prop(self, "active_connection")
|
|
||||||
layout.prop(self, "local_folder")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def connect_to_ftp(domain, login, passwd, active = True) :
|
|
||||||
from ftplib import FTP
|
|
||||||
ftp = FTP(domain,timeout = 10)
|
|
||||||
ftp.login(user = login, passwd = passwd)
|
|
||||||
ftp.set_pasv(active)
|
|
||||||
|
|
||||||
return ftp
|
|
||||||
|
|
||||||
|
|
||||||
def is_exclude(name, patterns) :
|
|
||||||
from fnmatch import fnmatch
|
|
||||||
|
|
||||||
if not isinstance(patterns, (list,tuple)) :
|
|
||||||
patterns = [patterns]
|
|
||||||
|
|
||||||
return any([fnmatch(name, p) for p in patterns])
|
|
||||||
|
|
||||||
|
|
||||||
def get_ftp_files(ftp, root, exclusions) :
|
|
||||||
files = []
|
|
||||||
|
|
||||||
ftp.cwd(root)
|
|
||||||
for k,v in ftp.mlsd() :
|
|
||||||
if is_exclude(k, exclusions) : continue
|
|
||||||
|
|
||||||
path = '/'.join([root,k])
|
|
||||||
if v['type'] == 'file' :
|
|
||||||
files.append(path)
|
|
||||||
|
|
||||||
elif v['type'] == 'dir' :
|
|
||||||
files+= get_ftp_files(ftp, path, exclusions)
|
|
||||||
|
|
||||||
return sorted(files)
|
|
||||||
|
|
||||||
def get_files(root, exclusions) :
|
|
||||||
'''Recursively get files in passed directory if not in exclusion list'''
|
|
||||||
|
|
||||||
files = []
|
|
||||||
for f in os.scandir(root) :
|
|
||||||
if is_exclude(f.path, exclusions) : continue
|
|
||||||
|
|
||||||
if f.is_file() :
|
|
||||||
files.append(f.path)
|
|
||||||
|
|
||||||
elif f.is_dir() :
|
|
||||||
files+= get_files(f.path, exclusions)
|
|
||||||
|
|
||||||
return sorted(files)
|
|
||||||
|
|
||||||
|
|
||||||
def createDirs(ftp, dirpath):
|
|
||||||
from ftplib import error_perm
|
|
||||||
|
|
||||||
"""
|
|
||||||
Create dir with subdirs (progressive dir creation).
|
|
||||||
|
|
||||||
:param ftp: connected FTP
|
|
||||||
:param dirpath: path (like 'test/test1/test2')
|
|
||||||
|
|
||||||
:type ftp: FTP
|
|
||||||
:type dirpath: str
|
|
||||||
:rtype: None
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
dirpath = dirpath.replace('\\', '/')
|
|
||||||
tmp = dirpath.split('/')
|
|
||||||
dirs = []
|
|
||||||
|
|
||||||
for _ in tmp:
|
|
||||||
if len(dirs) == 0:
|
|
||||||
dirs.append(_)
|
|
||||||
continue
|
|
||||||
|
|
||||||
dirs.append(dirs[-1] + '/' + _)
|
|
||||||
|
|
||||||
for _ in dirs:
|
|
||||||
try:
|
|
||||||
ftp.mkd(_)
|
|
||||||
except error_perm as e:
|
|
||||||
e_str = str(e)
|
|
||||||
if '550' in e_str and 'File exists' in e_str:
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
def exist_ftp_file(ftp, filepath) :
|
|
||||||
filepath = filepath.replace('\\', '/')
|
|
||||||
#ftp.cwd('/')
|
|
||||||
|
|
||||||
if filepath.startswith('/') : filepath = filepath[1:]
|
|
||||||
|
|
||||||
split_path = filepath.split('/')
|
|
||||||
|
|
||||||
for i,path in enumerate(split_path) :
|
|
||||||
list_dir = {k:v['type'] for k,v in ftp.mlsd()}
|
|
||||||
#print(i,path, list(list_dir.keys()))
|
|
||||||
if path in list_dir.keys() :
|
|
||||||
if i == len(split_path)-1 :
|
|
||||||
return True
|
|
||||||
|
|
||||||
elif list_dir[path] == 'dir' :
|
|
||||||
ftp.cwd(path)
|
|
||||||
else :
|
|
||||||
return False
|
|
||||||
else :
|
|
||||||
return False
|
|
||||||
|
|
||||||
class SendToFtp(Operator):
|
|
||||||
"""Send To FTP"""
|
|
||||||
bl_idname = "ftpsync.send"
|
|
||||||
bl_label = "Send to FTP"
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
prefs = context.preferences.addons[__name__].preferences
|
|
||||||
ftp = connect_to_ftp(prefs.domain, prefs.login, prefs.password, prefs.active_connection)
|
|
||||||
|
|
||||||
print('\nStart Uploading to the FTP')
|
|
||||||
print('Source Folder :', prefs.local_folder)
|
|
||||||
|
|
||||||
ftp_folder = prefs.ftp_folder.replace('\\','/').strip('/')
|
|
||||||
if not ftp_folder.startswith('/') : ftp_folder = '/'+ftp_folder
|
|
||||||
|
|
||||||
local_folder = prefs.local_folder.replace('\\','/')
|
|
||||||
|
|
||||||
|
|
||||||
for f in get_files(local_folder, EXCLUSIONS) :
|
|
||||||
'''recursively get files in directory if not in exclusion list'''
|
|
||||||
|
|
||||||
ftp.cwd(ftp_folder)
|
|
||||||
dst_file = re.sub(local_folder, '', f.replace('\\','/')).strip('/')
|
|
||||||
|
|
||||||
if prefs.sync_type == 'ONLY_NEW' and exist_ftp_file(ftp,dst_file) :
|
|
||||||
continue
|
|
||||||
|
|
||||||
ftp.cwd(ftp_folder)
|
|
||||||
|
|
||||||
createDirs(ftp,dirname(dst_file))
|
|
||||||
|
|
||||||
try :
|
|
||||||
with open(f, 'rb') as fp:
|
|
||||||
ftp.storbinary('STOR '+dst_file, fp)
|
|
||||||
|
|
||||||
print('New file :', dst_file)
|
|
||||||
except :
|
|
||||||
print('Impossible to copy %s'%fp)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ftp.close()
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
|
|
||||||
class GetFromFtp(Operator,):
|
|
||||||
"""Create a new Mesh Object"""
|
|
||||||
bl_idname = "ftpsync.get"
|
|
||||||
bl_label = "Get from FTP"
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
prefs = context.preferences.addons[__name__].preferences
|
|
||||||
ftp = connect_to_ftp(prefs.domain, prefs.login, prefs.password, prefs.active_connection)
|
|
||||||
|
|
||||||
if not prefs.local_folder :
|
|
||||||
print('You have to set the local folder in your prefs')
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
print('\nStart Downloading from FTP')
|
|
||||||
print('Destination Folder :', prefs.local_folder)
|
|
||||||
|
|
||||||
ftp_folder = prefs.ftp_folder.replace('\\','/').strip('/')
|
|
||||||
if not ftp_folder.startswith('/') : ftp_folder = '/'+ftp_folder
|
|
||||||
|
|
||||||
local_folder = prefs.local_folder.replace('\\','/')
|
|
||||||
|
|
||||||
for f in get_ftp_files(ftp, ftp_folder, EXCLUSIONS) :
|
|
||||||
dst_file = join(local_folder, re.sub(ftp_folder,'',f.replace('\\','/')).strip('/'))
|
|
||||||
|
|
||||||
if prefs.sync_type == 'ONLY_NEW' and exists(dst_file) :
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not exists(dirname(dst_file)) :
|
|
||||||
os.makedirs(dirname(dst_file))
|
|
||||||
|
|
||||||
with open(dst_file, 'wb') as fp:
|
|
||||||
ftp.retrbinary('RETR '+f, fp.write)
|
|
||||||
|
|
||||||
print('New file :', dst_file)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ftp.close()
|
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
|
|
||||||
class Menu(bpy.types.Menu):
|
|
||||||
bl_label = "ADM"
|
|
||||||
bl_idname = "ADM_MT_menu"
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
prefs = context.preferences.addons[__name__].preferences
|
|
||||||
|
|
||||||
layout.operator("ftpsync.get", icon='IMPORT')
|
|
||||||
layout.operator("ftpsync.send", icon='EXPORT')
|
|
||||||
layout.prop(prefs, "sync_type", text = '')
|
|
||||||
|
|
||||||
def menu_draw(self,context):
|
|
||||||
self.layout.menu("ADM_MT_menu")
|
|
||||||
|
|
||||||
cls = [Preferences, SendToFtp, GetFromFtp,Menu]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
|
||||||
bpy.types.TOPBAR_MT_editor_menus.append(menu_draw)
|
|
||||||
for c in cls :
|
|
||||||
bpy.utils.register_class(c)
|
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
|
||||||
bpy.types.TOPBAR_MT_editor_menus.remove(menu_draw)
|
|
||||||
for c in cls :
|
|
||||||
bpy.utils.unregister_class(c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
register()
|
|
|
@ -1,51 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
import bpy
|
|
||||||
import bmesh
|
|
||||||
from math import degrees, radians
|
|
||||||
|
|
||||||
ob = bpy.context.object
|
|
||||||
me = ob.data
|
|
||||||
|
|
||||||
if ob.type != 'MESH':
|
|
||||||
print('ERROR : not a mesh')
|
|
||||||
|
|
||||||
mode = bpy.context.mode
|
|
||||||
|
|
||||||
if mode == 'EDIT_MESH':
|
|
||||||
#me = bpy.context.edit_object.data
|
|
||||||
bm = bmesh.from_edit_mesh(me) #get Bmesh from edit
|
|
||||||
|
|
||||||
elif mode == 'OBJECT':
|
|
||||||
bm = bmesh.new() # create an empty BMesh
|
|
||||||
bm.from_mesh(me) # fill it in from a Mesh
|
|
||||||
|
|
||||||
bm.verts.ensure_lookup_table()
|
|
||||||
bm.edges.ensure_lookup_table()
|
|
||||||
bm.faces.ensure_lookup_table()
|
|
||||||
|
|
||||||
#deselect everything first
|
|
||||||
for f in bm.edges:f.select = False
|
|
||||||
for e in bm.edges:e.select = False
|
|
||||||
for v in bm.verts:v.select = False
|
|
||||||
|
|
||||||
#keep vertex that mark angle above this degree tolerance
|
|
||||||
degree_tolerance = 20
|
|
||||||
tol = radians(degree_tolerance)
|
|
||||||
|
|
||||||
#Checker
|
|
||||||
ct = 0
|
|
||||||
for v in bm.verts:
|
|
||||||
if len(v.link_edges) == 2:#star/end of chain
|
|
||||||
if v.calc_edge_angle() < tol:
|
|
||||||
#v.select_set(True)#full select
|
|
||||||
v.select_set(ct%2)#checker select
|
|
||||||
else:
|
|
||||||
ct = 0#reset counter
|
|
||||||
ct+=1
|
|
||||||
|
|
||||||
if mode == 'EDIT_MESH':
|
|
||||||
bmesh.update_edit_mesh(me, True)
|
|
||||||
|
|
||||||
elif mode == 'OBJECT':
|
|
||||||
bm.to_mesh(me)
|
|
||||||
bm.free()
|
|
|
@ -1,17 +0,0 @@
|
||||||
vl = [10,11,12,13,14,15,16,17,18,19,20,21,22,23]
|
|
||||||
vl = [10,11,12,13,14]
|
|
||||||
print()
|
|
||||||
|
|
||||||
divider = 0.8
|
|
||||||
vnum = len(vl)
|
|
||||||
#limit = int(vnum/divider)
|
|
||||||
limit = int(vnum*divider)
|
|
||||||
rest = vnum - limit
|
|
||||||
print("vnum", vnum)#Dbg
|
|
||||||
print("limit", limit)#Dbg
|
|
||||||
print("rest", rest)#Dbg
|
|
||||||
|
|
||||||
for i, vid in enumerate(vl):
|
|
||||||
total = (vnum-limit-1)
|
|
||||||
w = (i - limit)/total
|
|
||||||
print(i, f'{w:.2f}')#Dbg
|
|
Loading…
Reference in New Issue