2024-03-05 21:33:30 +09:00
|
|
|
'''
|
2024-03-11 22:26:26 +09:00
|
|
|
Copyright (C) 2021 - 2024 Akaneyu
|
2024-03-05 21:33:30 +09:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
'''
|
|
|
|
|
|
|
|
import os
|
|
|
|
import ctypes
|
|
|
|
import bpy
|
|
|
|
import bpy.utils.previews
|
|
|
|
import blf
|
|
|
|
import numpy as np
|
|
|
|
from . ui_renderer import UIRenderer as UIRenderer
|
|
|
|
|
|
|
|
class Session:
|
|
|
|
def __init__(self):
|
|
|
|
self.icons = None
|
|
|
|
self.ui_renderer = None
|
|
|
|
self.draw_handler = None
|
|
|
|
self.previous_object = None
|
|
|
|
self.brush_position = None
|
|
|
|
self.brush_size = 50.0
|
|
|
|
self.selecting_direction = False
|
|
|
|
self.brush_active = False
|
|
|
|
self.resizing_brush = False
|
|
|
|
|
|
|
|
def get_session():
|
|
|
|
global session
|
|
|
|
|
|
|
|
return session
|
|
|
|
|
|
|
|
def draw_handler():
|
|
|
|
global session
|
|
|
|
|
|
|
|
context = bpy.context
|
|
|
|
|
|
|
|
wm = context.window_manager
|
|
|
|
props = wm.symmetrizetexture_properties
|
|
|
|
mirror_axis = props.image_mirror_axis
|
|
|
|
|
|
|
|
info_text = None
|
|
|
|
|
|
|
|
if not session.ui_renderer:
|
|
|
|
session.ui_renderer = UIRenderer()
|
|
|
|
|
|
|
|
# direction setup
|
|
|
|
if session.selecting_direction:
|
|
|
|
if mirror_axis == 'x_axis':
|
|
|
|
border_pos1 = context.region.view2d.view_to_region(0.5, 0, clip=False)
|
|
|
|
border_pos2 = context.region.view2d.view_to_region(0.5, 1.0, clip=False)
|
|
|
|
else:
|
|
|
|
border_pos1 = context.region.view2d.view_to_region(0, 0.5, clip=False)
|
|
|
|
border_pos2 = context.region.view2d.view_to_region(1.0, 0.5, clip=False)
|
|
|
|
|
|
|
|
session.ui_renderer.render_border(border_pos1, border_pos2)
|
|
|
|
|
|
|
|
center = context.region.view2d.view_to_region(0.5, 0.5, clip=False)
|
|
|
|
|
|
|
|
if mirror_axis == 'x_axis':
|
|
|
|
arrow_angle = 0 if session.direction > 0 else np.pi
|
|
|
|
else:
|
|
|
|
arrow_angle = np.pi / 2.0 if session.direction > 0 else np.pi * 1.5
|
|
|
|
|
|
|
|
session.ui_renderer.render_arrow(center, arrow_angle)
|
|
|
|
|
2024-03-11 22:26:26 +09:00
|
|
|
info_text = "[LMB] Perform " \
|
|
|
|
+ "[RMB] Cancel"
|
2024-03-05 21:33:30 +09:00
|
|
|
|
|
|
|
# brush
|
|
|
|
if session.brush_active and session.brush_position:
|
|
|
|
session.ui_renderer.render_brush_frame(session.brush_position, session.brush_size)
|
|
|
|
|
2024-03-11 22:26:26 +09:00
|
|
|
info_text = "[LMB] Perform " \
|
|
|
|
+ "[RMB] Finish " \
|
|
|
|
+ "[F] Change brush size"
|
2024-03-05 21:33:30 +09:00
|
|
|
|
2024-03-11 22:26:26 +09:00
|
|
|
area_width, area_height = context.area.width, context.area.height
|
2024-03-05 21:33:30 +09:00
|
|
|
|
|
|
|
# info text
|
|
|
|
if info_text:
|
2024-03-11 22:26:26 +09:00
|
|
|
ui_scale = context.preferences.system.ui_scale
|
2024-03-05 21:33:30 +09:00
|
|
|
|
2024-03-11 22:26:26 +09:00
|
|
|
session.ui_renderer.render_info_box((0, 0), (area_width, 20 * ui_scale))
|
2024-03-05 21:33:30 +09:00
|
|
|
|
2024-03-11 22:26:26 +09:00
|
|
|
blf.position(0, 8 * ui_scale, 6 * ui_scale, 0)
|
|
|
|
blf.size(0, 11 * ui_scale) if bpy.app.version >= (3, 6) \
|
|
|
|
else blf.size(0, 11 * ui_scale, 72)
|
|
|
|
blf.color(0, 0.7, 0.7, 0.7, 1.0)
|
2024-03-05 21:33:30 +09:00
|
|
|
blf.draw(0, info_text)
|
|
|
|
|
|
|
|
def load_icons():
|
|
|
|
global session
|
|
|
|
|
|
|
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
icons = bpy.utils.previews.new()
|
|
|
|
|
|
|
|
icons_dir = os.path.join(script_dir, "icons")
|
|
|
|
for file_name in os.listdir(icons_dir):
|
|
|
|
icon_name = os.path.splitext(file_name)[0]
|
|
|
|
icons.load(icon_name, os.path.join(icons_dir, file_name), 'IMAGE')
|
|
|
|
|
|
|
|
session.icons = icons
|
|
|
|
|
|
|
|
def dispose_icons():
|
|
|
|
global session
|
|
|
|
|
|
|
|
bpy.utils.previews.remove(session.icons)
|
|
|
|
|
|
|
|
def load_native_library():
|
|
|
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
|
|
|
if os.name == 'nt':
|
|
|
|
lib_file_name = 'symmetrize_texture.dll'
|
|
|
|
else:
|
|
|
|
lib_file_name = 'libsymmetrize_texture.so'
|
|
|
|
|
|
|
|
lib = ctypes.CDLL(os.path.join(script_dir, lib_file_name))
|
|
|
|
|
|
|
|
return lib
|
|
|
|
|
|
|
|
def unload_native_library(lib):
|
|
|
|
if os.name == 'nt':
|
|
|
|
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
|
|
|
|
kernel32.FreeLibrary.argtypes = [ctypes.c_void_p]
|
|
|
|
kernel32.FreeLibrary(lib._handle)
|
|
|
|
else:
|
|
|
|
stdlib = ctypes.CDLL("")
|
|
|
|
stdlib.dlclose.argtypes = [ctypes.c_void_p]
|
|
|
|
stdlib.dlclose(lib._handle)
|
|
|
|
|
|
|
|
def get_image_previews(self, context):
|
|
|
|
image_previews = []
|
|
|
|
for i, img in enumerate(bpy.data.images):
|
|
|
|
image_previews.append((img.name, img.name, img.name, bpy.types.UILayout.icon(img), i))
|
|
|
|
|
|
|
|
return image_previews
|
|
|
|
|
|
|
|
class SYMMETRIZE_TEXTURE_PropertyGroup(bpy.types.PropertyGroup):
|
|
|
|
image_preview: bpy.props.EnumProperty(items=get_image_previews, options={'LIBRARY_EDITABLE'})
|
|
|
|
|
|
|
|
# created in the register(): image_mirror_axis
|
|
|
|
|
|
|
|
brush_strength: bpy.props.FloatProperty(name='Strength', default=1.0, min=0, max=1.0, precision=3)
|
|
|
|
brush_falloff: bpy.props.EnumProperty(items=(
|
|
|
|
('smooth', 'Smooth', 'Smooth', 'SMOOTHCURVE', 0),
|
|
|
|
('constant', 'Constant', 'Constant', 'NOCURVE', 1)))
|
|
|
|
|
|
|
|
session = Session()
|