From 11029606dfe204b2722f8a3f490650f05956748a Mon Sep 17 00:00:00 2001 From: ChristopheSeux Date: Thu, 28 Sep 2023 12:54:37 +0200 Subject: [PATCH] refacto to use import_planes as a functions --- __init__.py | 5 +- fn.py => core.py | 7 +- export_psd_layers.py | 8 +- import_planes/__init__.py | 13 ++ import_planes/core.py | 158 ++++++++++++++++++++++ import_planes/operators.py | 130 ++++++++++++++++++ operators/__init__.py | 3 +- operators/convert_planes.py | 25 ++-- operators/import_planes.py | 263 ------------------------------------ operators/manage_objects.py | 18 +-- operators/manage_planes.py | 6 +- ui/ui_list.py | 16 +-- 12 files changed, 344 insertions(+), 308 deletions(-) rename fn.py => core.py (99%) create mode 100644 import_planes/__init__.py create mode 100644 import_planes/core.py create mode 100644 import_planes/operators.py delete mode 100644 operators/import_planes.py diff --git a/__init__.py b/__init__.py index 38756e2..19acdb7 100644 --- a/__init__.py +++ b/__init__.py @@ -16,11 +16,12 @@ import bpy from pathlib import Path from . import operators +from . import import_planes from . import export_psd_layers from . import ui from . import preferences -from . import fn +from . import core #from . file_utils import install_module #install_module('psd_tools', 'psd-tools') @@ -69,7 +70,7 @@ def register(): for m in modules: m.register() - preferences.ui_in_sidebar_update(fn.get_addon_prefs(), bpy.context) + preferences.ui_in_sidebar_update(core.get_addon_prefs(), bpy.context) def unregister(): for m in reversed(modules): diff --git a/fn.py b/core.py similarity index 99% rename from fn.py rename to core.py index 41e7fad..6f1d8dd 100644 --- a/fn.py +++ b/core.py @@ -453,14 +453,12 @@ def create_gpencil_reference( return gps_new def import_image_as_gp_reference( - context: bpy.types.Context, image, pack_image: bool = False, ): """ Import image from `image` as a textured rectangle in the given grease pencil object. - :param context: The active context. :param image: The image filepath or image datablock :param pack_image: Whether to pack the image into the Blender file. """ @@ -468,7 +466,7 @@ def import_image_as_gp_reference( if not image: return - scene = context.scene + scene = bpy.context.scene if not isinstance(image, bpy.types.Image): image = bpy.data.images.load(str(image), check_existing=True) @@ -806,4 +804,5 @@ def coord_distance_from_cam(coord, context=None, camera=None): camera = camera or context.scene.camera if not camera: return - return (coord - camera.matrix_world.to_translation()).length \ No newline at end of file + return (coord - camera.matrix_world.to_translation()).length + diff --git a/export_psd_layers.py b/export_psd_layers.py index d11a9b8..7484af6 100644 --- a/export_psd_layers.py +++ b/export_psd_layers.py @@ -7,7 +7,7 @@ import json #import psd_tools from pathlib import Path from time import time -from . import fn +from . import core from . file_utils import install_module @@ -83,7 +83,7 @@ def export_psd(psd_file, output=None, scale=0.5): l.visible = True name = layer.name - norm_name = fn.norm_str(name, padding=2) # Gadget normstr + norm_name = core.norm_str(name, padding=2) # Gadget normstr print('name: ', name) png_output = (output/norm_name).with_suffix('.png') @@ -269,8 +269,8 @@ class BPM_OT_export_psd_layers(bpy.types.Operator, ImportHelper): return {"FINISHED"} -classes=( -BPM_OT_export_psd_layers, +classes = ( + BPM_OT_export_psd_layers, ) def register(): diff --git a/import_planes/__init__.py b/import_planes/__init__.py new file mode 100644 index 0000000..9e59432 --- /dev/null +++ b/import_planes/__init__.py @@ -0,0 +1,13 @@ +from . import operators + +modules = ( + operators, +) + +def register(): + for module in modules: + bpy.utils.register_class(module) + +def unregister(): + for module in reversed(modules): + bpy.utils.unregister_class(module) \ No newline at end of file diff --git a/import_planes/core.py b/import_planes/core.py new file mode 100644 index 0000000..c37be1e --- /dev/null +++ b/import_planes/core.py @@ -0,0 +1,158 @@ + +import bpy +import json +from pathlib import Path +import os +import re +import time +from pathlib import Path +from mathutils import Vector, geometry +from pprint import pprint as pp +from ..constants import PREFIX, BGCOL, INIT_POS +from ..core import (create_cam, set_collection, get_image_infos_from_object, get_col, + import_image_as_gp_reference, create_image_plane, set_collection, get_image, + create_empty_image, create_plane_holder, create_plane_driver, reload_bg_list) + + +def get_json_infos(json_path) -> tuple((list, tuple)): + '''return a tuple with (image paths list, [x, y] original psd resolution''' + + import json + setup_json_path = Path(json_path) + setup_json = json.loads(setup_json_path.read_text()) + + # psd = Path(setup_json['psd']) # path to psd + # image_size = setup_json['image_size'] # image size (reduced) + # png_dir = Path(setup_json['png_dir']) # path to png directory + layers = setup_json['layers'] # dic : name, lbbox, bbox, index, path + org_image_size = setup_json['org_image_size'] # original PSD dimensions + img_list = [Path(l['path']) for l in layers] # (png_dir /l['name']).with_suffix('.png') + + return img_list, org_image_size + + +def setup_bg_camera(image_size, scene=None, cam_type=None): + '''Create background camera from scene active camera according to background image resolution + image_size :: + cam_type :: wether the BG camera should be set to perspective or orthographic + ''' + + scn = scene or bpy.context.scene + + movie_resolution = scn.render.resolution_x, scn.render.resolution_y + anim_cam = scn.camera + anim_cam_scale = 6 # anim_cam.data.ortho_scale + anim_cam_focale = 50 # anim_cam.data.lens + cam_type = cam_type or anim_cam.data.type + + ## scale up plane scale and bg_cam ortho_scale according to film res + bg_factor = (anim_cam_scale * image_size[0]) / movie_resolution[0] + #print(f'({anim_cam_scale} * {image_size[0]}) / {movie_resolution[0]} = {bg_factor}') + + ## scale down the focal according to film res + bg_focale = (anim_cam_focale * movie_resolution[0]) / image_size[0] + #print(f'({anim_cam_focale} * {movie_resolution[0]}) / {image_size[0]} = {bg_focale}') + + bg_cam = scn.objects.get('bg_cam') + if not bg_cam: + bg_cam = create_cam(name='bg_cam', type=cam_type, size=bg_factor, focale=bg_focale) + bg_cam['resolution'] = image_size + + bg_cam.matrix_world = anim_cam.matrix_world + + bg_cam.hide_select = True + bg_cam.lock_location = bg_cam.lock_rotation = bg_cam.lock_scale = [True]*3 + + set_collection(bg_cam, 'Camera') + set_collection(anim_cam, 'Camera') + return bg_cam + +def import_planes(images, import_type='GPENCIL', mode='REPLACE', image_size=None): + scn = bpy.context.scene + + existing_backgrounds = [(o, *img_info) for o in scn.objects \ + if o.get('is_background') and (img_info := get_image_infos_from_object(o)) and o.get('is_background') and o.parent] + + existing_backgrounds.sort(key=lambda x: x[0].parent.location.z, reverse=True) # sort by parent (holder) location Z + + far_plane = INIT_POS + if existing_backgrounds: + far_plane = existing_backgrounds[-1][0].parent.location.z + + ## Ensure bg_cam setup (option to use active camera ?) + bg_cam = scn.objects.get('bg_cam') + if not bg_cam: + if not image_size: + ## Get image size from first file + img = bpy.data.images.load(str(images[0]), check_existing=True) + image_size = (img.size[0], img.size[1]) + bg_cam = setup_bg_camera(image_size) + + ## Ensure Background collection + backgrounds = get_col(BGCOL) # Create if needed + + for image_path in images: + image_path = Path(image_path) + print(f'Importing {image_path.name}') + current_holder = None + + file_stem = image_path.stem + # current_bg = next((o for o if o.parent and get_image_infos_from_object(o)), None) + current_bg = next((o for o in existing_backgrounds if Path(o[1].filepath) == image_path), None) + if current_bg: + current_holder = current_bg.parent + # TODO store opacity or delete existing objects only after loop + + if current_bg and mode == 'SKIP': + print(f' - SKIP: existing {current_bg.name}') + continue + + if current_bg and mode == 'REPLACE': + print(f' - DEL: existing {current_bg.name}') + bpy.data.objects.remove(current_bg) + + # Import image + if import_type == 'GPENCIL': + bg_img = import_image_as_gp_reference( + image=str(image_path), + pack_image=False, + ) + elif import_type == 'MESH': + bg_img = create_image_plane(image_path) + + elif import_type == 'EMPTY': + bg_img = create_empty_image(image_path) + + + bg_img.name = file_stem + '_texgp' + bg_img.hide_select = True + + if current_holder: + bg_img.parent = current_holder + set_collection(bg_img, current_holder.users_collection[0].name) + continue + + # img, opacity = get_image_infos_from_object(bg_img) + print('bg_img: ', bg_img) + img = get_image(bg_img) + # bg_name = clean_image_name(img.name) + file_stem = file_stem + + # Get create collection from image clean name + bg_col = get_col(file_stem, parent=backgrounds) + + ## Set in collection + bg_col.objects.link(bg_img) + + print('img: ', img.name, bg_img.name, bg_img.users_collection) + + ## create the holder, parent to camera, set driver and set collection. Could also pass 'fp' + holder = create_plane_holder(img, name=PREFIX + file_stem, parent=bg_cam, link_in_col=bg_col) + print('holder: ', holder.name) + + create_plane_driver(holder, bg_cam, distance=far_plane) + bg_img.parent = holder + + far_plane += 2 + + reload_bg_list(scene=scn) \ No newline at end of file diff --git a/import_planes/operators.py b/import_planes/operators.py new file mode 100644 index 0000000..4c66d3c --- /dev/null +++ b/import_planes/operators.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +## Standalone based on SPA code +## Code from create_gp_texture_ref.py (Copyright (C) 2023, The SPA Studios. All rights reserved.) +## adapted to create as a single 'GP as image' and fit in camera with driver + +import bpy +from bpy_extras.io_utils import ImportHelper +from bpy.props import (StringProperty, + CollectionProperty, + BoolProperty, + EnumProperty) +import json +import bpy_extras +from mathutils import Vector +import os +from pathlib import Path +from .. import core +from .. constants import * +from .. export_psd_layers import export_psd_bg +from . core import import_planes, get_json_infos + + + + + +class BPM_OT_import_bg_images(bpy.types.Operator, ImportHelper): + bl_idname = "bpm.import_bg_images" + bl_label = "Import Background images" + bl_description = "Import either a Json, a PSD, multiple files" + bl_options = {"REGISTER"} # , "INTERNAL" + + # filename_ext = '.json' + + filter_glob: StringProperty( + default=';'.join([f'*{i}' for i in bpy.path.extensions_image]) + ';*.json', + options={'HIDDEN'} ) + + ## Active selection + filepath : StringProperty( + name="File Path", + description="File path used for import", + maxlen= 1024) # the active file + + ## Handle multi-selection + files: CollectionProperty( + name="File Path", + type=bpy.types.OperatorFileListElement, + ) # The filelist collection + + ## Choice to place before or after ? + + import_type : EnumProperty( + name="Import As", description="Type of import to ", default='GPENCIL', options={'ANIMATABLE'}, + items=( + ('GPENCIL', 'Gpencil Object', 'Import bg planes as gpencil objects', 0), + ('EMPTY', 'Empty Reference', 'Import bg planes as empty objects', 1), + ('MESH', 'Texture Plane', 'Import bg planes as mesh objects', 2), + )) + + mode : EnumProperty( + name="Mode", description="", default='REPLACE', options={'ANIMATABLE'}, + items=( + ('REPLACE', 'Replace Existing', 'Replace the image if already exists', 0), + ('SKIP', 'Skip Existing', 'Skip the import if the image alreaady exists in planes', 1), + # ('PURGE', 'Purge', 'When object exists, fully delete it before linking, even associated collection and holder', 2), + )) + + def execute(self, context): + org_image_size = None + scn = context.scene + active_file = Path(self.filepath) + + if len(self.files) == 1 and active_file.suffix.lower() in ('.json', '.psd'): + json_path = None + print('active_file.suffix.lower(): ', active_file.suffix.lower()) + if active_file.suffix.lower() == '.psd': + print('Is a PSD') + ## Export layers and create json setup file + # Export passes in a 'render' or 'render_hd' folder aside psd + json_path = export_psd_bg(str(active_file)) + + elif active_file.suffix.lower() == '.json': + print('Is a json') + # Use json data to batch import + json_path = active_file + + if not json_path: + self.report({'ERROR'}, 'No json path to load, you can try loading from psd or selecting image file directly') + return {'CANCELLED'} + + file_list, org_image_size = get_json_infos(json_path) + folder = Path(json_path).parent # render folder + + else: + folder = active_file.parent + # Filter out json (we may want ot import the PSD as imagelisted in bpy.path.extensions_image) + file_list = [folder / f.name for f in self.files if Path(f.name).suffix in bpy.path.extensions_image] + + if not file_list: + self.report({'ERROR'}, 'Image file list is empty') + return {'CANCELLED'} + + + ## simple_list + # existing_backgrounds = [o for o in context.scene.objects if o.get('is_background')] + # existing_holders = [o for o in context.scene.objects if o.name.startswith(PREFIX) and o.type == 'MESH' and o.children] + + ## Has dict + # existing_backgrounds = {o : img_info for o in context.scene.objects if o.get('is_background') and (img_info := o.get('is_background'))} + + # FIXME: Use existing background custom prop? : o.get('is_background') + ## list of Tuples : [(plane_object, img_data, transparency_value), ...] + + import_planes(file_list, import_type=self.import_type, image_size=org_image_size) + + + return {"FINISHED"} + +classes = ( + BPM_OT_import_bg_images, +) + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) \ No newline at end of file diff --git a/operators/__init__.py b/operators/__init__.py index 9118b33..029148b 100644 --- a/operators/__init__.py +++ b/operators/__init__.py @@ -1,11 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later -from . import (manage_objects, manage_planes, import_planes, convert_planes) +from . import (manage_objects, manage_planes, convert_planes) modules = ( manage_objects, manage_planes, - import_planes, convert_planes ) diff --git a/operators/convert_planes.py b/operators/convert_planes.py index dce56d3..b535244 100644 --- a/operators/convert_planes.py +++ b/operators/convert_planes.py @@ -6,7 +6,7 @@ import re # from bpy_extras.io_utils import ImportHelper from pathlib import Path from bpy.types import Operator -from .. import fn +from .. import core from .. constants import BGCOL, MODULE_DIR ## Plane conversion between multiple types: GP plane / image plane / empty references @@ -29,7 +29,7 @@ def convert_background_image(ob, target = 'GPENCIL'): return # Get info from Source - img_infos = fn.get_image_infos_from_object(ob) + img_infos = core.get_image_infos_from_object(ob) if not img_infos: print(f'No could not retrieve image source from {ob}') return @@ -51,8 +51,7 @@ def convert_background_image(ob, target = 'GPENCIL'): suffix = '' if target == 'GPENCIL': ## Create GP plane from Texture source - new = fn.import_image_as_gp_reference( - bpy.context, + new = core.import_image_as_gp_reference( image_data, pack_image=False, ) @@ -62,24 +61,24 @@ def convert_background_image(ob, target = 'GPENCIL'): ## HARDCODED text blend path: # blend = '/s/blender/blender-2.9_scripts/addons/bg_plane_manager/texture_plane.blend' blend = str(MODULE_DIR / 'texture_plane.blend') - node_group = fn.link_nodegroup(blend, 'texture_plane', link=False) - new = fn.create_image_plane(image_data, node_group) + node_group = core.link_nodegroup(blend, 'texture_plane', link=False) + new = core.create_image_plane(image_data, node_group) suffix = '_texplane' if target == 'EMPTY': - new = fn.create_empty_image(image_data) + new = core.create_empty_image(image_data) suffix = '_texempty' # Strip after '.' or get original name ? print('source name: ', new.name) - new.name = fn.clean_image_name(image_data.name) + suffix + new.name = core.clean_image_name(image_data.name) + suffix print('final name:', new.name) new['is_background'] = True ## Without holder ## Transfer attributes - # fn.create_plane_driver(new, cam) + # core.create_plane_driver(new, cam) # new.parent = cam # new['distance'] = ob['distance'] @@ -102,13 +101,13 @@ def convert_background_image(ob, target = 'GPENCIL'): ## Transfer current transparency # Destination - fn.set_opacity(new, opacity) + core.set_opacity(new, opacity) ## Set in collection if not len(ob.users_collection): - fn.set_collection(new, BGCOL) + core.set_collection(new, BGCOL) else: - fn.set_collection(new, ob.users_collection[0].name) + core.set_collection(new, ob.users_collection[0].name) ## Remove old object bpy.data.objects.remove(ob) @@ -136,7 +135,7 @@ class BPM_OT_convert_planes(Operator): def execute(self, context): print('BPM_OT_convert_planes') - holder_list = fn.scan_backgrounds(all_bg=True) + holder_list = core.scan_backgrounds(all_bg=True) if not holder_list: self.report({'ERROR'}, 'No Background found to convert, Structure must have a parent holder') return {'CANCELLED'} diff --git a/operators/import_planes.py b/operators/import_planes.py deleted file mode 100644 index ea061ab..0000000 --- a/operators/import_planes.py +++ /dev/null @@ -1,263 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -## Standalone based on SPA code -## Code from create_gp_texture_ref.py (Copyright (C) 2023, The SPA Studios. All rights reserved.) -## adapted to create as a single 'GP as image' and fit in camera with driver - -import bpy -from bpy_extras.io_utils import ImportHelper -from bpy.props import (StringProperty, - CollectionProperty, - BoolProperty, - EnumProperty) -import json -import bpy_extras -from mathutils import Vector -import os -from pathlib import Path -from .. import fn -from .. constants import * -from .. export_psd_layers import export_psd_bg - - -def get_json_infos(json_path) -> tuple((list, tuple)): - '''return a tuple with (image paths list, [x, y] original psd resolution''' - - import json - setup_json_path = Path(json_path) - setup_json = json.loads(setup_json_path.read_text()) - - # psd = Path(setup_json['psd']) # path to psd - # image_size = setup_json['image_size'] # image size (reduced) - # png_dir = Path(setup_json['png_dir']) # path to png directory - layers = setup_json['layers'] # dic : name, lbbox, bbox, index, path - org_image_size = setup_json['org_image_size'] # original PSD dimensions - img_list = [Path(l['path']) for l in layers] # (png_dir /l['name']).with_suffix('.png') - - return img_list, org_image_size - -def setup_bg_camera(image_size, context=None, scn=None, cam_type=None): - '''Create background camera from scene active camera according to background image resolution - image_size :: - cam_type :: wether the BG camera should be set to perspective or orthographic - ''' - context = context or bpy.context - scn=scn or context.scene - - movie_resolution = scn.render.resolution_x, scn.render.resolution_y - anim_cam = scn.camera - anim_cam_scale = 6 # anim_cam.data.ortho_scale - anim_cam_focale = 50 # anim_cam.data.lens - cam_type = cam_type or anim_cam.data.type - - ## scale up plane scale and bg_cam ortho_scale according to film res - bg_factor = (anim_cam_scale * image_size[0]) / movie_resolution[0] - #print(f'({anim_cam_scale} * {image_size[0]}) / {movie_resolution[0]} = {bg_factor}') - - ## scale down the focal according to film res - bg_focale = (anim_cam_focale * movie_resolution[0]) / image_size[0] - #print(f'({anim_cam_focale} * {movie_resolution[0]}) / {image_size[0]} = {bg_focale}') - - bg_cam = scn.objects.get('bg_cam') - if not bg_cam: - bg_cam = fn.create_cam(name='bg_cam', type=cam_type, size=bg_factor, focale=bg_focale) - bg_cam['resolution'] = image_size - - bg_cam.matrix_world = anim_cam.matrix_world - - bg_cam.hide_select = True - bg_cam.lock_location = bg_cam.lock_rotation = bg_cam.lock_scale = [True]*3 - - fn.set_collection(bg_cam, 'Camera') - fn.set_collection(anim_cam, 'Camera') - return bg_cam - -class BPM_OT_import_bg_images(bpy.types.Operator, ImportHelper): - bl_idname = "bpm.import_bg_images" - bl_label = "Import Background images" - bl_description = "Import either a Json, a PSD, multiple files" - bl_options = {"REGISTER"} # , "INTERNAL" - - # filename_ext = '.json' - - filter_glob: StringProperty( - default=';'.join([f'*{i}' for i in bpy.path.extensions_image]) + ';*.json', - options={'HIDDEN'} ) - - ## Active selection - filepath : StringProperty( - name="File Path", - description="File path used for import", - maxlen= 1024) # the active file - - ## Handle multi-selection - files: CollectionProperty( - name="File Path", - type=bpy.types.OperatorFileListElement, - ) # The filelist collection - - ## Choice to place before or after ? - - import_type : EnumProperty( - name="Import As", description="Type of import to ", default='GPENCIL', options={'ANIMATABLE'}, - items=( - ('GPENCIL', 'Gpencil Object', 'Import bg planes as gpencil objects', 0), - ('EMPTY', 'Empty Reference', 'Import bg planes as empty objects', 1), - ('MESH', 'Texture Plane', 'Import bg planes as mesh objects', 2), - )) - - mode : EnumProperty( - name="Mode", description="", default='REPLACE', options={'ANIMATABLE'}, - items=( - ('REPLACE', 'Replace Existing', 'Replace the image if already exists', 0), - ('SKIP', 'Skip Existing', 'Skip the import if the image alreaady exists in planes', 1), - # ('PURGE', 'Purge', 'When object exists, fully delete it before linking, even associated collection and holder', 2), - )) - - def execute(self, context): - org_image_size = None - scn = context.scene - active_file = Path(self.filepath) - - if len(self.files) == 1 and active_file.suffix.lower() in ('.json', '.psd'): - json_path = None - print('active_file.suffix.lower(): ', active_file.suffix.lower()) - if active_file.suffix.lower() == '.psd': - print('Is a PSD') - ## Export layers and create json setup file - # Export passes in a 'render' or 'render_hd' folder aside psd - json_path = export_psd_bg(str(active_file)) - - elif active_file.suffix.lower() == '.json': - print('Is a json') - # Use json data to batch import - json_path = active_file - - if not json_path: - self.report({'ERROR'}, 'No json path to load, you can try loading from psd or selecting image file directly') - return {'CANCELLED'} - - file_list, org_image_size = get_json_infos(json_path) - folder = Path(json_path).parent # render folder - - else: - folder = active_file.parent - # Filter out json (we may want ot import the PSD as imagelisted in bpy.path.extensions_image) - file_list = [folder / f.name for f in self.files if Path(f.name).suffix in bpy.path.extensions_image] - - if not file_list: - self.report({'ERROR'}, 'Image file list is empty') - return {'CANCELLED'} - - - ## simple_list - # existing_backgrounds = [o for o in context.scene.objects if o.get('is_background')] - # existing_holders = [o for o in context.scene.objects if o.name.startswith(PREFIX) and o.type == 'MESH' and o.children] - - ## Has dict - # existing_backgrounds = {o : img_info for o in context.scene.objects if o.get('is_background') and (img_info := o.get('is_background'))} - - # FIXME: Use existing background custom prop? : o.get('is_background') - ## list of Tuples : [(plane_object, img_data, transparency_value), ...] - - existing_backgrounds = [(o, *img_info) for o in context.scene.objects \ - if o.get('is_background') and (img_info := fn.get_image_infos_from_object(o)) and o.get('is_background') and o.parent] - - existing_backgrounds.sort(key=lambda x: x[0].parent.location.z, reverse=True) # sort by parent (holder) location Z - - far_plane = INIT_POS - if existing_backgrounds: - far_plane = existing_backgrounds[-1][0].parent.location.z - - ## Ensure bg_cam setup (option to use active camera ?) - bg_cam = scn.objects.get('bg_cam') - if not bg_cam: - if not org_image_size: - ## Get image size from first file - img = bpy.data.images.load(file_list[0], check_existing=True) - org_image_size = (img.size[0], img.size[1]) - bg_cam = setup_bg_camera( - org_image_size, context=context, scn=scn, cam_type=scn.camera.data.type) - - ## Ensure Background collection - backgrounds = fn.get_col(BGCOL) # Create if needed - - for fp in file_list: - print(f'Importing {fp.name}') - current_holder = None - - file_stem = Path(fp).stem - # current_bg = next((o for o if o.parent and fn.get_image_infos_from_object(o)), None) - current_bg = next((o for o in existing_backgrounds if o[1].filepath == fp), None) - if current_bg: - current_holder = current_bg.parent - # TODO store opacity or delete existing objects only after loop - - if current_bg and self.mode == 'SKIP': - print(f' - SKIP: existing {current_bg.name}') - continue - - if current_bg and self.mode == 'REPLACE': - print(f' - DEL: existing {current_bg.name}') - bpy.data.objects.remove(current_bg) - - # Import image - if self.import_type == 'GPENCIL': - bg_img = fn.import_image_as_gp_reference( - context=context, - image=fp, - pack_image=False, - ) - elif self.import_type == 'MESH': - bg_img = fn.create_image_plane(fp) - - elif self.import_type == 'EMPTY': - bg_img = fn.create_empty_image(fp) - - - bg_img.name = file_stem + '_texgp' - bg_img.hide_select = True - - if current_holder: - bg_img.parent = current_holder - fn.set_collection(bg_img, current_holder.users_collection[0].name) - continue - - # img, opacity = fn.get_image_infos_from_object(bg_img) - print('bg_img: ', bg_img) - img = fn.get_image(bg_img) - # bg_name = fn.clean_image_name(img.name) - file_stem = file_stem - - # Get create collection from image clean name - bg_col = fn.get_col(file_stem, parent=backgrounds) - - ## Set in collection - bg_col.objects.link(bg_img) - - print('img: ', img.name, bg_img.name, bg_img.users_collection) - - ## create the holder, parent to camera, set driver and set collection. Could also pass 'fp' - holder = fn.create_plane_holder(img, name=PREFIX + file_stem, parent=bg_cam, link_in_col=bg_col) - print('holder: ', holder.name) - - fn.create_plane_driver(holder, bg_cam, distance=far_plane) - bg_img.parent = holder - - far_plane += 2 - - fn.reload_bg_list(scene=scn) - self.report({'INFO'}, f'Settings loaded from: {os.path.basename(self.filepath)}') - return {"FINISHED"} - -classes=( -BPM_OT_import_bg_images, -) - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - -def unregister(): - for cls in reversed(classes): - bpy.utils.unregister_class(cls) \ No newline at end of file diff --git a/operators/manage_objects.py b/operators/manage_objects.py index f7e29db..2807f33 100644 --- a/operators/manage_objects.py +++ b/operators/manage_objects.py @@ -6,7 +6,7 @@ import mathutils from bpy.types import Operator from mathutils import Matrix, Vector -from .. import fn +from .. import core def set_resolution_from_cam_prop(cam=None): @@ -292,8 +292,8 @@ class BPM_OT_create_and_place_in_camera(Operator): self.create = False ## Set placeholder name (Comment to let an empty string) - self.name = fn.placeholder_name(self.name, context) - prefs = fn.get_addon_prefs() + self.name = core.placeholder_name(self.name, context) + prefs = core.get_addon_prefs() self.use_light = prefs.use_light self.edit_line_opacity = prefs.edit_line_opacity @@ -306,9 +306,9 @@ class BPM_OT_create_and_place_in_camera(Operator): if settings.planes and settings.planes[settings.index].plane: plane = settings.planes[settings.index].plane - self.distance = fn.coord_distance_from_cam_straight(plane.matrix_world.to_translation()) - 0.005 + self.distance = core.coord_distance_from_cam_straight(plane.matrix_world.to_translation()) - 0.005 else: - self.distance = fn.coord_distance_from_cam_straight(context.scene.cursor.location) + self.distance = core.coord_distance_from_cam_straight(context.scene.cursor.location) self.distance = max([1.0, self.distance]) # minimum one meter away from cam if self.create: @@ -331,10 +331,10 @@ class BPM_OT_create_and_place_in_camera(Operator): def execute(self, context): if self.create: - ob_name = fn.placeholder_name(self.name, context) + ob_name = core.placeholder_name(self.name, context) ## Create Object - prefs = fn.get_addon_prefs() + prefs = core.get_addon_prefs() gp_data = bpy.data.grease_pencils.new(ob_name) ob = bpy.data.objects.new(ob_name, gp_data) ob.use_grease_pencil_lights = prefs.use_light @@ -342,7 +342,7 @@ class BPM_OT_create_and_place_in_camera(Operator): l = gp_data.layers.new('GP_Layer') l.frames.new(context.scene.frame_current) - fn.set_collection(ob, 'GP') # Gpencils + core.set_collection(ob, 'GP') # Gpencils # Add to bg_plane collection new_item = context.scene.bg_props.planes.add() @@ -351,7 +351,7 @@ class BPM_OT_create_and_place_in_camera(Operator): # Set active on last context.scene.bg_props.index = len(context.scene.bg_props.planes) - 1 - fn.gp_transfer_mode(ob) + core.gp_transfer_mode(ob) loaded_palette = False if hasattr(bpy.types, "GPTB_OT_load_default_palette"): diff --git a/operators/manage_planes.py b/operators/manage_planes.py index 676f681..1415248 100644 --- a/operators/manage_planes.py +++ b/operators/manage_planes.py @@ -5,7 +5,7 @@ from bpy.types import Operator from mathutils import Vector from os.path import abspath -from .. import fn +from .. import core from .. constants import BGCOL ## Open image folder @@ -39,7 +39,7 @@ class BPM_OT_open_bg_folder(Operator): self.report({'ERROR'}, f'Could not found child for holder: {ob.name}') return {"CANCELLED"} - img = fn.get_image(tex_obj) + img = core.get_image(tex_obj) fp = Path(abspath(bpy.path.abspath(img.filepath))) if not fp.exists(): self.report({'ERROR'}, f'Not found: {fp}') @@ -290,7 +290,7 @@ class BPM_OT_reload(Operator): return True def execute(self, context): - error = fn.reload_bg_list() + error = core.reload_bg_list() if error: if isinstance(error, list): self.report({'WARNING'}, 'Wrong name for some object, see console:' + '\n'.join(error)) diff --git a/ui/ui_list.py b/ui/ui_list.py index 72f11a2..21a2bb0 100644 --- a/ui/ui_list.py +++ b/ui/ui_list.py @@ -1,6 +1,6 @@ import bpy from pathlib import Path -from .. import fn +from .. import core from .. constants import PREFIX from bpy.types import UIList, PropertyGroup, Menu from bpy.props import (PointerProperty, @@ -51,7 +51,7 @@ class BPM_UL_bg_list(UIList): return - layercol = context.view_layer.layer_collection.children['Background'].children.get(fn.clean_image_name(item.plane.name[len(PREFIX):])) + layercol = context.view_layer.layer_collection.children['Background'].children.get(core.clean_image_name(item.plane.name[len(PREFIX):])) if not layercol: layout.label(text=f'{item.plane.name} (problem with name)', icon='ERROR') return @@ -63,7 +63,7 @@ class BPM_UL_bg_list(UIList): layout.label(text=f'{item.plane.name} (No children)', icon='ERROR') ## Image preview - image = fn.get_image(item.plane.children[0]) + image = core.get_image(item.plane.children[0]) # layout.label(icon_value=image.preview_ensure().icon_id) # layout.template_icon(icon_value=image.preview_ensure().icon_id) @@ -169,7 +169,7 @@ class BPM_UL_bg_list(UIList): ## TODO: Find a better solution to get the collection ## Get Collection from plane name -> problem: prefix "BG_" and suffix - layercol = context.view_layer.layer_collection.children['Background'].children.get(fn.clean_image_name(item.plane.name[len(PREFIX):])) + layercol = context.view_layer.layer_collection.children['Background'].children.get(core.clean_image_name(item.plane.name[len(PREFIX):])) if not layercol: layout.label(text=f'{item.plane.name} (problem with name)', icon='ERROR') return @@ -181,7 +181,7 @@ class BPM_UL_bg_list(UIList): layout.label(text=f'{item.plane.name} (No children)', icon='ERROR') ## Image preview - image = fn.get_image(item.plane.children[0]) + image = core.get_image(item.plane.children[0]) # layout.label(icon_value=image.preview_ensure().icon_id) # layout.template_icon(icon_value=image.preview_ensure().icon_id) @@ -270,7 +270,7 @@ def update_opacity(self, context): for ob in pool: if not ob or not ob.children: continue - fn.set_opacity(ob.children[0], opacity=context.scene.bg_props.opacity) + core.set_opacity(ob.children[0], opacity=context.scene.bg_props.opacity) def update_on_index_change(self, context): # print('index change:', context.scene.bg_props.index) @@ -287,13 +287,13 @@ def update_on_index_change(self, context): plane = item.plane if not plane.children: return - opacity = fn.get_opacity(plane.children[0]) + opacity = core.get_opacity(plane.children[0]) if not opacity: return props['opacity'] = opacity elif item.type == 'obj': - fn.gp_transfer_mode(item.plane, context) + core.gp_transfer_mode(item.plane, context) def update_select(self, context):