refacto to use import_planes as a functions
parent
ba5084e458
commit
11029606df
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
@ -807,3 +805,4 @@ def coord_distance_from_cam(coord, context=None, camera=None):
|
|||
if not camera:
|
||||
return
|
||||
return (coord - camera.matrix_world.to_translation()).length
|
||||
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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'}
|
||||
|
|
|
@ -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)
|
|
@ -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"):
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue