fix: reset draw cam rotation
1.7.8 - fix: reset rotation in draw cam mode keep view in the same place (counter camera rotation) - code: initial enhancement for palette linkinggpv2
parent
9389faed22
commit
78b70c8fca
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
1.7.8
|
||||
|
||||
- fix: reset rotation in draw cam mode keep view in the same place (counter camera rotation)
|
||||
- code: initial enhancement for palette linking
|
||||
|
||||
1.7.7
|
||||
|
||||
- feat: add copy path to `check link` ops with multiple path representation choices
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
from time import ctime
|
||||
import bpy
|
||||
import mathutils
|
||||
from mathutils import Vector#, Matrix
|
||||
from pathlib import Path
|
||||
import math
|
||||
from math import radians
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
class GPTB_OT_copy_text(bpy.types.Operator):
|
||||
bl_idname = "wm.copytext"
|
||||
bl_label = "Copy to clipboard"
|
||||
|
@ -280,6 +284,89 @@ class GPTB_OT_set_view_as_cam(bpy.types.Operator):
|
|||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GPTB_OT_reset_cam_rot(bpy.types.Operator):
|
||||
bl_idname = "gp.reset_cam_rot"
|
||||
bl_label = "Reset rotation"
|
||||
bl_description = "Reset rotation of the draw manipulation camera"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.scene.camera and not context.scene.camera.name.startswith('Cam')
|
||||
# return context.region_data.view_perspective == 'CAMERA'# check if in camera
|
||||
|
||||
def get_center_view(self, context, cam):
|
||||
from bpy_extras.view3d_utils import location_3d_to_region_2d
|
||||
frame = cam.data.view_frame()
|
||||
mat = cam.matrix_world
|
||||
frame = [mat @ v for v in frame]
|
||||
frame_px = [location_3d_to_region_2d(context.region, context.space_data.region_3d, v) for v in frame]
|
||||
center_x = frame_px[2].x + (frame_px[0].x - frame_px[2].x)/2
|
||||
center_y = frame_px[1].y + (frame_px[0].y - frame_px[1].y)/2
|
||||
return mathutils.Vector((center_x, center_y))
|
||||
|
||||
def get_ui_ratio(self, context):
|
||||
'''correct ui overlap from header/toolbars'''
|
||||
regs = context.area.regions
|
||||
if context.preferences.system.use_region_overlap:
|
||||
w = context.area.width
|
||||
# minus tool header
|
||||
h = context.area.height - regs[0].height
|
||||
else:
|
||||
# minus tool leftbar + sidebar right
|
||||
w = context.area.width - regs[2].width - regs[3].width
|
||||
# minus tool header + header
|
||||
h = context.area.height - regs[0].height - regs[1].height
|
||||
|
||||
self.ratio = h / w
|
||||
self.ratio_inv = w / h
|
||||
|
||||
def execute(self, context):
|
||||
cam = context.scene.camera
|
||||
|
||||
# self.cam_init_euler = self.cam.rotation_euler.copy()
|
||||
if not cam.parent or cam.parent.type != 'CAMERA':
|
||||
self.report({'ERROR'}, "No parents to refer to for rotation reset")
|
||||
return {"CANCELLED"}
|
||||
|
||||
# store original rotation mode
|
||||
org_rotation_mode = cam.rotation_mode
|
||||
|
||||
# set to euler to works with quaternions, restored at finish
|
||||
cam.rotation_mode = 'XYZ'
|
||||
# store camera matrix world
|
||||
org_cam_matrix = cam.matrix_world.copy()
|
||||
|
||||
org_cam_z = cam.rotation_euler.z
|
||||
|
||||
## initialize current view_offset in camera
|
||||
view_cam_offset = mathutils.Vector(context.space_data.region_3d.view_camera_offset)
|
||||
|
||||
# Do the reset to parent transforms
|
||||
cam.matrix_world = cam.parent.matrix_world # wrong, get the parent rotation offset
|
||||
|
||||
# Get diff angle
|
||||
angle = cam.rotation_euler.z - org_cam_z
|
||||
# create rotation matrix with negative angle (we want to counter the move)
|
||||
neg = -angle
|
||||
rot_mat2d = mathutils.Matrix([[math.cos(neg), -math.sin(neg)], [math.sin(neg), math.cos(neg)]])
|
||||
|
||||
# restore original rotation mode
|
||||
cam.rotation_mode = org_rotation_mode
|
||||
|
||||
self.get_ui_ratio(context)
|
||||
# apply rotation matrix
|
||||
new_cam_offset = view_cam_offset.copy()
|
||||
new_cam_offset = mathutils.Vector((new_cam_offset[0], new_cam_offset[1] * self.ratio)) # apply screen ratio
|
||||
new_cam_offset.rotate(rot_mat2d)
|
||||
new_cam_offset = mathutils.Vector((new_cam_offset[0], new_cam_offset[1] * self.ratio_inv)) # restore screen ratio
|
||||
|
||||
context.space_data.region_3d.view_camera_offset = new_cam_offset
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
""" # old simple cam draw fit to cam
|
||||
class GPTB_OT_reset_cam_rot(bpy.types.Operator):
|
||||
bl_idname = "gp.reset_cam_rot"
|
||||
bl_label = "Reset rotation"
|
||||
|
@ -306,9 +393,8 @@ class GPTB_OT_reset_cam_rot(bpy.types.Operator):
|
|||
else:
|
||||
self.report({'ERROR'}, "No parents to refer to for rotation reset")
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
return {"FINISHED"}
|
||||
"""
|
||||
|
||||
class GPTB_OT_toggle_mute_animation(bpy.types.Operator):
|
||||
bl_idname = "gp.toggle_mute_animation"
|
||||
|
|
|
@ -5,7 +5,6 @@ from bpy_extras.io_utils import ImportHelper, ExportHelper
|
|||
from pathlib import Path
|
||||
from .utils import convert_attr, get_addon_prefs
|
||||
|
||||
|
||||
### --- Json serialized material load/save
|
||||
|
||||
def load_palette(context, filepath):
|
||||
|
@ -58,7 +57,7 @@ class GPTB_OT_load_default_palette(bpy.types.Operator):
|
|||
line.name = 'line'
|
||||
|
||||
# load json
|
||||
pfp = Path(bpy.path.abspath(get_addon_prefs().palette_path))
|
||||
pfp = Path(bpy.path.abspath(get_addon_prefs().palettes_path))
|
||||
if not pfp.exists():
|
||||
self.report({'ERROR'}, f'Palette path not found')
|
||||
return {"CANCELLED"}
|
||||
|
@ -166,7 +165,7 @@ class GPTB_OT_save_palette(bpy.types.Operator, ExportHelper):
|
|||
def load_blend_palette(context, filepath):
|
||||
'''Load materials on current active object from current chosen blend'''
|
||||
#from pathlib import Path
|
||||
#palette_fp = C.preferences.addons['GP_toolbox'].preferences['palette_path']
|
||||
#palette_fp = C.preferences.addons['GP_toolbox'].preferences['palettes_path']
|
||||
#fp = Path(palette_fp) / 'christina.blend'
|
||||
print(f'-- import palette from : {filepath} --')
|
||||
for ob in context.selected_objects:
|
||||
|
@ -460,7 +459,6 @@ class GPTB_OT_clean_material_stack(bpy.types.Operator):
|
|||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = (
|
||||
GPTB_OT_load_palette,
|
||||
GPTB_OT_save_palette,
|
||||
|
@ -468,6 +466,7 @@ GPTB_OT_load_default_palette,
|
|||
GPTB_OT_load_blend_palette,
|
||||
GPTB_OT_copy_active_to_selected_palette,
|
||||
GPTB_OT_clean_material_stack,
|
||||
|
||||
)
|
||||
|
||||
def register():
|
||||
|
|
|
@ -0,0 +1,338 @@
|
|||
import bpy
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
from bpy_extras.io_utils import ImportHelper, ExportHelper
|
||||
from pathlib import Path
|
||||
from . import utils
|
||||
# from . import blendfile
|
||||
|
||||
from bpy.types import (
|
||||
Panel,
|
||||
Operator,
|
||||
PropertyGroup,
|
||||
UIList,
|
||||
)
|
||||
|
||||
from bpy.props import (
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
StringProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
PointerProperty,
|
||||
)
|
||||
|
||||
class GPTB_UL_blend_list(UIList):
|
||||
# order_by_distance : BoolProperty(default=True)
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
layout.label(text=item.blend_name)
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
row = layout.row()
|
||||
subrow = row.row(align=True)
|
||||
subrow.prop(self, "filter_name", text="") # Only show items matching this name (use ‘*’ as wildcard)
|
||||
|
||||
# reverse order
|
||||
icon = 'SORT_DESC' if self.use_filter_sort_reverse else 'SORT_ASC'
|
||||
subrow.prop(self, "use_filter_sort_reverse", text="", icon=icon) # built-in reverse
|
||||
|
||||
def filter_items(self, context, data, propname):
|
||||
# example : https://docs.blender.org/api/blender_python_api_current/bpy.types.UIList.html
|
||||
# This function gets the collection property (as the usual tuple (data, propname)), and must return two lists:
|
||||
# * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the
|
||||
# matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the
|
||||
# * The second one is for reordering, it must return a list containing the new indices of the items (which
|
||||
# gives us a mapping org_idx -> new_idx).
|
||||
# Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).
|
||||
# If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than
|
||||
# returning full lists doing nothing!).
|
||||
|
||||
collec = getattr(data, propname)
|
||||
helper_funcs = bpy.types.UI_UL_list
|
||||
|
||||
# Default return values.
|
||||
flt_flags = []
|
||||
flt_neworder = []
|
||||
|
||||
|
||||
# Filtering by name #not working damn !
|
||||
if self.filter_name:
|
||||
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, collec, "name",
|
||||
reverse=self.use_filter_sort_reverse)#self.use_filter_name_reverse)
|
||||
|
||||
return flt_flags, flt_neworder
|
||||
|
||||
class GPTB_UL_object_list(UIList):
|
||||
# order_by_distance : BoolProperty(default=True)
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
layout.label(text=item.name)
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
row = layout.row()
|
||||
subrow = row.row(align=True)
|
||||
subrow.prop(self, "filter_name", text="") # Only show items matching this name (use ‘*’ as wildcard)
|
||||
# reverse order
|
||||
icon = 'SORT_DESC' if self.use_filter_sort_reverse else 'SORT_ASC'
|
||||
subrow.prop(self, "use_filter_sort_reverse", text="", icon=icon) # built-in reverse
|
||||
|
||||
def filter_items(self, context, data, propname):
|
||||
collec = getattr(data, propname)
|
||||
helper_funcs = bpy.types.UI_UL_list
|
||||
# Default return values.
|
||||
flt_flags = []
|
||||
flt_neworder = []
|
||||
if self.filter_name:
|
||||
flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, collec, "name",
|
||||
reverse=self.use_filter_sort_reverse)
|
||||
return flt_flags, flt_neworder
|
||||
|
||||
|
||||
def reload_blends(self, context):
|
||||
scn = context.scene
|
||||
pl_prop = scn.bl_palettes_props
|
||||
uilist = scn.bl_palettes_props.blends
|
||||
uilist.clear()
|
||||
pl_prop['bl_idx'] = 0
|
||||
|
||||
prefs = utils.get_addon_prefs()
|
||||
|
||||
if pl_prop.use_project_path:
|
||||
palette_fp = prefs.palette_path
|
||||
else:
|
||||
palette_fp = pl_prop.custom_dir
|
||||
|
||||
if not palette_fp: # singular
|
||||
item = uilist.add()
|
||||
item.blend_name = 'No Palette Path Specified'
|
||||
reload_objects(self, context)
|
||||
return
|
||||
|
||||
palettes_dir = Path(palette_fp)
|
||||
if not palettes_dir.exists():
|
||||
item = uilist.add()
|
||||
item.blend_name = 'Palette Path not found'
|
||||
reload_objects(self, context)
|
||||
return
|
||||
|
||||
blends = [(o.path, Path(o).stem, "") for o in os.scandir(palettes_dir)
|
||||
if o.is_file()
|
||||
and o.name.endswith('.blend')
|
||||
and re.search(r'v\d{3}', o.name, re.I)]
|
||||
|
||||
blends.sort(key=lambda x: x[1], reverse=False)
|
||||
# print('blends found', len(blends))
|
||||
|
||||
for bl in blends: # populate list
|
||||
item = uilist.add()
|
||||
|
||||
scn.bl_palettes_props['bl_idx'] = len(uilist) - 1 # don't trigger updates
|
||||
item.blend_path = bl[0]
|
||||
item.blend_name = bl[1]
|
||||
|
||||
scn.bl_palettes_props.bl_idx = len(uilist) - 1 # trigger update ()
|
||||
# reload_objects(self, context) # triggered by above assignation
|
||||
|
||||
# return len(blends) # return value must be None
|
||||
|
||||
class GPTB_OT_palettes_reload_blends(Operator):
|
||||
bl_idname = "gp.palettes_reload_blends"
|
||||
bl_label = "Reload Palette Blends"
|
||||
bl_description = "Reload the blends in UI list of palettes linker"
|
||||
bl_options = {"REGISTER"} # , "INTERNAL"
|
||||
|
||||
def execute(self, context):
|
||||
reload_blends(self, context)
|
||||
# ret = reload_blends(self, context)
|
||||
# if ret is None:
|
||||
# self.report({'ERROR'}, 'No blend scanned, check palette path')
|
||||
# else:
|
||||
# self.report({'INFO'}, f'{ret} blends found')
|
||||
return {"FINISHED"}
|
||||
|
||||
def reload_objects(self, context):
|
||||
scn = context.scene
|
||||
prefs = utils.get_addon_prefs()
|
||||
pal_prop = scn.bl_palettes_props
|
||||
blend_uil = pal_prop.blends
|
||||
obj_uil = pal_prop.objects
|
||||
obj_uil.clear()
|
||||
pal_prop['ob_idx'] = 0
|
||||
|
||||
if not len(blend_uil) or (len(blend_uil) == 1 and not bool(blend_uil[0].blend_path)):
|
||||
item = obj_uil.add()
|
||||
item.name = 'No blend to list object'
|
||||
return
|
||||
|
||||
if not blend_uil[pal_prop.bl_idx].blend_path:
|
||||
item = obj_uil.add()
|
||||
item.name = 'Selected blend has no path'
|
||||
return
|
||||
|
||||
path_to_blend = Path(blend_uil[pal_prop.bl_idx].blend_path)
|
||||
|
||||
ob_list = utils.check_objects_in_blend(str(path_to_blend)) # get list of string of all object except camera
|
||||
|
||||
ob_list.sort(reverse=False) # filter object by name ?
|
||||
# remove camera
|
||||
# print('blends found', len(blends))
|
||||
|
||||
for ob_name in ob_list: # populate list
|
||||
item = obj_uil.add()
|
||||
item.name = ob_name
|
||||
item.path = str(path_to_blend / 'Object' / ob_name)
|
||||
|
||||
pal_prop.ob_idx = len(obj_uil) - 1
|
||||
# return len(ob_list) # must return None if used in update
|
||||
|
||||
|
||||
## PROPS
|
||||
|
||||
class GPTB_PG_blend_prop(PropertyGroup):
|
||||
blend_name : StringProperty() # stem of the path
|
||||
blend_path : StringProperty() # ful path
|
||||
# select: BoolProperty(update=update_select) # use and update to set the plane selection
|
||||
|
||||
class GPTB_PG_object_prop(PropertyGroup):
|
||||
name : StringProperty() # stem of the path
|
||||
path : StringProperty() # Object / Material ?
|
||||
|
||||
class GPTB_PG_palette_settings(PropertyGroup):
|
||||
bl_idx : IntProperty(update=reload_objects) # update_on_index_change to reload object
|
||||
blends : bpy.props.CollectionProperty(type=GPTB_PG_blend_prop)
|
||||
|
||||
ob_idx : IntProperty()
|
||||
objects : bpy.props.CollectionProperty(type=GPTB_PG_object_prop)
|
||||
|
||||
use_project_path : BoolProperty(name='Use Project Palettes',
|
||||
default=True, description='Use palettes directory specified in gp toolbox addon preferences',
|
||||
update=reload_blends)
|
||||
|
||||
show_path : BoolProperty(name='Show path',
|
||||
default=True, description='Show Palette directoty filepath')
|
||||
|
||||
custom_dir : StringProperty(name='Custom Palettes Directory', subtype='DIR_PATH',
|
||||
description='Use choosen directory to load blend palettes',
|
||||
update=reload_blends)
|
||||
|
||||
import_type : EnumProperty(
|
||||
name="Import Type", description="Choose inmport type: link, append, append reuse (keep existing materials)",
|
||||
default='LINK', options={'ANIMATABLE'}, update=None, get=None, set=None,
|
||||
items=(
|
||||
('LINK', 'Link', 'Link materials to selected object', 0),
|
||||
('APPEND', 'Append', 'Append materials to selected objects', 1),
|
||||
('APPEND_REUSE', 'Append (Reuse)', 'Append materials to selected objects\nkeep those already there', 2),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
# fav_blend: StringProperty() ## mark a blend as prefered ? (need to be stored in prefereneces to restore in other blend...)
|
||||
|
||||
class GPTB_OT_import_obj_palette(Operator):
|
||||
bl_idname = "gp.import_obj_palette"
|
||||
bl_label = "Import Object Palette"
|
||||
bl_description = "Import object palette from blend"
|
||||
bl_options = {"REGISTER", "INTERNAL"}
|
||||
|
||||
def execute(self, context):
|
||||
pl_prop = context.scene.bl_palettes_props
|
||||
path = pl_prop.objects[pl_prop.ob_idx].path
|
||||
self.report({'INFO'}, f'Path to object: {path}')
|
||||
return {"FINISHED"}
|
||||
|
||||
## PANEL
|
||||
|
||||
class GPTB_PT_palettes_linker_ui(Panel):
|
||||
bl_idname = 'GPTB_PT_palettes_linker_ui'
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Gpencil"#Dev
|
||||
bl_label = "Palettes Mat Linker"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scn = bpy.context.scene
|
||||
pl_prop = scn.bl_palettes_props
|
||||
col= layout.column()
|
||||
prefs = utils.get_addon_prefs()
|
||||
## Here put the path thing (only to use a non-library)
|
||||
|
||||
# maybe in submenu...
|
||||
row = col.row()
|
||||
expand_icon = 'TRIA_DOWN' if pl_prop.show_path else 'TRIA_RIGHT'
|
||||
row.prop(pl_prop, 'show_path', text='', icon=expand_icon, emboss=False)
|
||||
row.prop(pl_prop, 'use_project_path', text='Use Project Palettes')
|
||||
row.operator("gp.palettes_reload_blends", icon="FILE_REFRESH", text="")
|
||||
|
||||
if pl_prop.use_project_path:
|
||||
## gp toolbox addon prefs path
|
||||
if not prefs.palette_path:
|
||||
col.label(text='Gp Toolbox Palette Directory Needed')
|
||||
col.label(text='(saved with preferences)')
|
||||
if pl_prop.show_path or not prefs.palette_path:
|
||||
col.prop(prefs, 'palette_path', text='Project Dir')
|
||||
else:
|
||||
## local path
|
||||
if not pl_prop.custom_dir:
|
||||
col.label(text='Need to specify directory')
|
||||
if pl_prop.show_path or not pl_prop.custom_dir:
|
||||
col.prop(pl_prop, 'custom_dir', text='Custom Dir')
|
||||
|
||||
|
||||
row = col.row()
|
||||
row.template_list("GPTB_UL_blend_list", "", pl_prop, "blends", pl_prop, "bl_idx",
|
||||
rows=2)
|
||||
# side panel
|
||||
# subcol = row.column(align=True)
|
||||
# subcol.operator("gp.palettes_reload_blends", icon="FILE_REFRESH", text="")
|
||||
|
||||
## Show object UI list only once blend Uilist is filled ?
|
||||
if not len(pl_prop.blends) or (len(pl_prop.blends) == 1 and not bool(pl_prop.blends[0].blend_path)):
|
||||
col.label(text='Select a blend to list available object')
|
||||
|
||||
row = col.row()
|
||||
row.template_list("GPTB_UL_object_list", "", pl_prop, "objects", pl_prop, "ob_idx",
|
||||
rows=4)
|
||||
|
||||
## Show link button in the border of the UI list ?
|
||||
# col.prop(pl_prop, 'import_type')
|
||||
split = col.split(align=True, factor=0.4 )
|
||||
split.prop(pl_prop, 'import_type', text='')
|
||||
|
||||
split.enabled = len(pl_prop.objects) and bool(pl_prop.objects[pl_prop.ob_idx].path)
|
||||
split.operator('gp.import_obj_palette', text='Palette')
|
||||
|
||||
# button to launch link with combined props (active only if the two items are valids)
|
||||
# str(Path(self.blends) / 'Object' / self.objects
|
||||
|
||||
|
||||
classes = (
|
||||
# blend list
|
||||
GPTB_PG_blend_prop,
|
||||
GPTB_UL_blend_list,
|
||||
GPTB_OT_palettes_reload_blends,
|
||||
|
||||
# object in blend list
|
||||
GPTB_PG_object_prop,
|
||||
GPTB_UL_object_list,
|
||||
|
||||
# prop containing two above
|
||||
GPTB_PG_palette_settings,
|
||||
|
||||
GPTB_OT_import_obj_palette,
|
||||
GPTB_PT_palettes_linker_ui,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
bpy.types.Scene.bl_palettes_props = bpy.props.PointerProperty(type=GPTB_PG_palette_settings)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
del bpy.types.Scene.bl_palettes_props
|
53
UI_tools.py
53
UI_tools.py
|
@ -308,8 +308,10 @@ class GPTB_PT_color(bpy.types.Panel):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
## Create empty frame on layer
|
||||
layout.operator('gp.create_empty_frames', icon='DECORATE_KEYFRAME')
|
||||
col.operator('gp.palette_linker', text=f'Link Materials Palette TO Object', icon='COLOR') ## ops
|
||||
col.operator('gp.create_empty_frames', icon='DECORATE_KEYFRAME')
|
||||
|
||||
""" # unused : added in Animation Manager
|
||||
class GPTB_PT_extra(bpy.types.Panel):
|
||||
|
@ -378,6 +380,48 @@ def expose_use_channel_color_pref(self, context):
|
|||
layout.label(text='Use Channel Colors (User preferences):')
|
||||
layout.prop(context.preferences.edit, 'use_anim_channel_group_colors')
|
||||
|
||||
def asset_browser_ui(self, context):
|
||||
'''Only shows in blender >= 3.0.0'''
|
||||
|
||||
layout = self.layout
|
||||
asset_file_handle = context.asset_file_handle
|
||||
if asset_file_handle is None:
|
||||
# layout.label(text="No asset selected", icon='INFO')
|
||||
layout.label(text='No object/material selected', icon='INFO')
|
||||
return
|
||||
if asset_file_handle.id_type not in ('OBJECT', 'MATERIAL'):
|
||||
layout.label(text='No object/material selected', icon='INFO')
|
||||
return
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
asset_library_ref = context.asset_library_ref
|
||||
## Path to blend
|
||||
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref)
|
||||
path_to_obj = Path(asset_lib_path) / 'Objects' / asset_file_handle.name
|
||||
|
||||
## respect header choice ?
|
||||
## import_type in (LINK, APPEND, APPEND_REUSE)
|
||||
imp_type = context.space_data.params.import_type
|
||||
if imp_type == 'APPEND':
|
||||
imp_txt = 'Append'
|
||||
elif imp_type == 'APPEND_REUSE':
|
||||
imp_txt = 'Append (Reuse)'
|
||||
else:
|
||||
imp_txt = 'Link'
|
||||
|
||||
if asset_file_handle.id_type == 'MATERIAL':
|
||||
layout.label(text=f'From Mat: {asset_file_handle.name}')
|
||||
if asset_file_handle.id_type == 'OBJECT':
|
||||
layout.label(text=f'From Obj: {asset_file_handle.name}')
|
||||
layout.label(text=f'{imp_txt} Materials To GP Object')
|
||||
layout.operator('gp.palette_linker', text=f'{imp_txt} Materials To GP Object') ## ops
|
||||
|
||||
# layout.label(text='Link Materials to GP Object')
|
||||
|
||||
|
||||
|
||||
classes = (
|
||||
GPTB_PT_sidebar_panel,
|
||||
GPTB_PT_checker,
|
||||
|
@ -394,9 +438,16 @@ def register():
|
|||
bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
|
||||
bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
|
||||
|
||||
# if bpy.app.version >= (3,0,0):
|
||||
# bpy.types.ASSETBROWSER_PT_metadata.append(asset_browser_ui)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
|
||||
bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
||||
# if bpy.app.version >= (3,0,0):
|
||||
# bpy.types.ASSETBROWSER_PT_metadata.remove(asset_browser_ui)
|
||||
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ bl_info = {
|
|||
"name": "GP toolbox",
|
||||
"description": "Set of tools for Grease Pencil in animation production",
|
||||
"author": "Samuel Bernou, Christophe Seux",
|
||||
"version": (1, 7, 7),
|
||||
"version": (1, 7, 8),
|
||||
"blender": (2, 91, 0),
|
||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||
"warning": "",
|
||||
|
@ -42,6 +42,7 @@ from . import OP_helpers
|
|||
from . import OP_keyframe_jump
|
||||
from . import OP_cursor_snap_canvas
|
||||
from . import OP_palettes
|
||||
from . import OP_palettes_linker
|
||||
from . import OP_brushes
|
||||
from . import OP_file_checker
|
||||
from . import OP_copy_paste
|
||||
|
@ -632,6 +633,7 @@ def register():
|
|||
OP_playblast_bg.register()
|
||||
OP_playblast.register()
|
||||
OP_palettes.register()
|
||||
# OP_palettes_linker.register()
|
||||
OP_brushes.register()
|
||||
OP_cursor_snap_canvas.register()
|
||||
OP_copy_paste.register()
|
||||
|
@ -678,6 +680,7 @@ def unregister():
|
|||
OP_copy_paste.unregister()
|
||||
OP_cursor_snap_canvas.unregister()
|
||||
OP_brushes.unregister()
|
||||
# OP_palettes_linker.unregister()
|
||||
OP_palettes.unregister()
|
||||
OP_file_checker.unregister()
|
||||
OP_helpers.unregister()
|
||||
|
|
17
utils.py
17
utils.py
|
@ -855,4 +855,19 @@ def draw_kmi(km, kmi, layout):
|
|||
# kmm = kc.keymaps.find_modal(kmi.idname)
|
||||
# if kmm:
|
||||
# draw_km(display_keymaps, kc, kmm, None, layout + 1)
|
||||
# layout.context_pointer_set("keymap", km)
|
||||
# layout.context_pointer_set("keymap", km)
|
||||
|
||||
def link_objects_in_blend(filepath, obj_name, link=True):
|
||||
'''Link an object by name from a file, if link is False, append instead of linking'''
|
||||
with bpy.data.libraries.load(filepath, link=link) as (data_from, data_to):
|
||||
data_to.objects = [o for o in data_from.objects if o == obj_name] # c.startswith(obj_name)
|
||||
return data_to.objects
|
||||
|
||||
def check_objects_in_blend(filepath, avoid_camera=True):
|
||||
'''return a list of object name in file'''
|
||||
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
|
||||
if avoid_camera:
|
||||
l = [o for o in data_from.objects if not 'camera' in o.lower()]
|
||||
else:
|
||||
l = [o for o in data_from.objects]
|
||||
return l
|
Loading…
Reference in New Issue