Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Adding an option for custom template path #643

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions ue2rigify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ def register():
# TODO remove this when new undo is stable
bpy.context.preferences.experimental.use_undo_legacy = True

templates.copy_default_templates()

# reload the submodules
if os.environ.get('UE2RIGIFY_DEV'):
for module in modules:
Expand All @@ -59,6 +57,7 @@ def register():
operators.register()
nodes.register()

templates.copy_default_templates()

def unregister():
"""
Expand Down
2 changes: 2 additions & 0 deletions ue2rigify/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright Epic Games, Inc. All Rights Reserved.
import os
import tempfile
import pathlib
from enum import Enum


Expand Down Expand Up @@ -32,6 +33,7 @@ class Nodes:


class Template:
CUSTOM_RIG_TEMPLATES_PATH = os.path.join(pathlib.Path().resolve(), 'resources', 'rig_templates')
RIG_TEMPLATES_PATH = os.path.join(tempfile.gettempdir(), ToolInfo.NAME.value, 'resources', 'rig_templates')
DEFAULT_MALE_TEMPLATE = 'male_mannequin'
DEFAULT_FEMALE_TEMPLATE = 'female_mannequin'
Expand Down
2 changes: 1 addition & 1 deletion ue2rigify/core/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ def set_meta_rig(self=None, context=None):
else:
remove_metarig()
create_meta_rig(self, file_path=os.path.join(
Template.RIG_TEMPLATES_PATH,
templates.get_custom_rig_template_path(),
self.selected_starter_metarig_template,
f'{Rigify.META_RIG_NAME}.py'
))
Expand Down
31 changes: 21 additions & 10 deletions ue2rigify/core/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
import bpy
import json
import shutil
import tempfile
from mathutils import Color, Euler, Matrix, Quaternion, Vector

from ..constants import Template, Modes, Rigify
from ..constants import Template, Modes, Rigify, ToolInfo
from . import scene
from . import utilities
from ..settings.tool_tips import *
Expand All @@ -24,7 +23,7 @@ def copy_default_templates():
Copies the default addon templates to the user location.
"""
template_location = os.path.join(os.path.dirname(__file__), os.path.pardir, 'resources', 'rig_templates')
shutil.copytree(template_location, Template.RIG_TEMPLATES_PATH, dirs_exist_ok=True)
shutil.copytree(template_location, get_custom_rig_template_path(), dirs_exist_ok=True)


def get_saved_node_data(properties):
Expand Down Expand Up @@ -187,7 +186,7 @@ def get_rig_templates(self=None, context=None, index_offset=0):
:return list: A list of tuples that define the rig template enumeration.
"""
rig_templates = []
rig_template_directories = next(os.walk(Template.RIG_TEMPLATES_PATH))[1]
rig_template_directories = next(os.walk(get_custom_rig_template_path()))[1]

# ensure that the male and female template are first
rig_templates.append((
Expand Down Expand Up @@ -233,7 +232,7 @@ def get_template_file_path(template_file_name, properties):
template_name = re.sub(r'\W+', '_', properties.new_template_name).lower()

return os.path.join(
Template.RIG_TEMPLATES_PATH,
get_custom_rig_template_path(),
template_name,
template_file_name
)
Expand Down Expand Up @@ -284,7 +283,7 @@ def remove_template_folder(properties, template):
properties.selected_mode = Modes.SOURCE.name

# delete the selected rig template folder
selected_template_path = os.path.join(Template.RIG_TEMPLATES_PATH, template)
selected_template_path = os.path.join(get_custom_rig_template_path(), template)

import logging
logging.info(selected_template_path)
Expand All @@ -302,10 +301,10 @@ def create_template_folder(template_name, properties):
:param object properties: The property group that contains variables that maintain the addon's correct state.
"""
# remove non alpha numeric characters
template_name = re.sub(r'\W+', '_', template_name.strip()).lower()
template_name = parse_template_name(template_name)

# create the template folder
template_path = os.path.join(Template.RIG_TEMPLATES_PATH, template_name)
template_path = os.path.join(get_custom_rig_template_path(), template_name)
if not os.path.exists(template_path):
try:
original_umask = os.umask(0)
Expand All @@ -320,6 +319,10 @@ def create_template_folder(template_name, properties):
return template_path


def parse_template_name(template_name):
return re.sub(r'\W+', '_', template_name.strip()).lower()


def populate_templates_dropdown(self=None, context=None):
"""
This function is called every time a the template dropdown is interacted with. It lists all the templates in the
Expand Down Expand Up @@ -410,14 +413,16 @@ def import_zip(zip_file_path, properties):
"""
# get the template name and path from the zip file
template_name = os.path.basename(zip_file_path).replace('.zip', '')
template_folder_path = os.path.join(Template.RIG_TEMPLATES_PATH, template_name)
template_folder_path = os.path.join(get_custom_rig_template_path(), template_name)

# create the template folder
create_template_folder(template_name, properties)

# unpack the zip file into the new template folder
shutil.unpack_archive(zip_file_path, template_folder_path, 'zip')

# select new template
properties.selected_rig_template = parse_template_name(template_name)

# TODO move to a shared location and define as a generic zip export
def export_zip(zip_file_path, properties):
Expand All @@ -431,7 +436,7 @@ def export_zip(zip_file_path, properties):
no_extension_file_path = zip_file_path.replace('.zip', '')

# zip up the folder and save it to the given path
template_folder_path = os.path.join(Template.RIG_TEMPLATES_PATH, properties.selected_export_template)
template_folder_path = os.path.join(get_custom_rig_template_path(), properties.selected_export_template)
shutil.make_archive(no_extension_file_path, 'zip', template_folder_path)

#
Expand Down Expand Up @@ -498,3 +503,9 @@ def safe_get_rig_templates(self, context):
global _result_reference_get_rig_templates
_result_reference_get_rig_templates = items
return items


def get_custom_rig_template_path():
main_prefs = bpy.context.preferences.addons.get(ToolInfo.NAME.value).preferences
template_path = main_prefs.custom_rig_template_path
return template_path
17 changes: 15 additions & 2 deletions ue2rigify/operators.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# Copyright Epic Games, Inc. All Rights Reserved.

import bpy
from .constants import Modes, Rigify
from .constants import Modes, Rigify, Template
from .ui import exporter
from .core import scene, nodes, templates, utilities
from bpy_extras.io_utils import ImportHelper


class SetDefault(bpy.types.Operator):
"""Set a default control rig mode"""
bl_idname = "ue2rigify.set_default"
bl_label = "Default"

def execute(self, context):
properties = bpy.context.scene.ue2rigify
properties.selected_mode = Modes.SOURCE.name
properties.selected_rig_template = Template.DEFAULT_MALE_TEMPLATE
return {'FINISHED'}


class ConvertToRigifyRig(bpy.types.Operator):
"""Convert the source rig to a control rig"""
bl_idname = "ue2rigify.convert_to_control_rig"
Expand Down Expand Up @@ -294,7 +306,8 @@ def execute(self, context):
ConstrainSourceToDeform,
RemoveConstraints,
SwitchModes,
NullOperator
NullOperator,
SetDefault
]


Expand Down
5 changes: 3 additions & 2 deletions ue2rigify/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,16 @@ class UE2RigifyProperties(bpy.types.PropertyGroup):
name="Metarig",
description=tool_tips.starter_metarig_template_tool_tip,
items=templates.safe_get_starter_metarig_templates,
update=scene.set_meta_rig
update=scene.set_meta_rig,
)

selected_rig_template: bpy.props.EnumProperty(
name="Rig Template",
description=tool_tips.rig_template_tool_tip,
items=templates.safe_populate_templates_dropdown,
options={'ANIMATABLE'},
update=templates.set_template
update=templates.set_template,
default=None
)

selected_mode: bpy.props.EnumProperty(
Expand Down
11 changes: 9 additions & 2 deletions ue2rigify/ui/addon_preferences.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# Copyright Epic Games, Inc. All Rights Reserved.

import bpy
from ..constants import ToolInfo

from ..constants import ToolInfo, Template

class Ue2RigifyAddonPreferences(bpy.types.AddonPreferences):
"""
This class subclasses the AddonPreferences class to create the addon preferences interface.
"""
bl_idname = ToolInfo.NAME.value

custom_rig_template_path: bpy.props.StringProperty(
name='Templates folder',
description='The location where your rig templates will be stored, including default templates',
subtype='DIR_PATH',
default=Template.CUSTOM_RIG_TEMPLATES_PATH
)

def draw(self, context):
"""
This function overrides the draw method in the AddonPreferences class. The draw method is the function
Expand All @@ -19,6 +25,7 @@ def draw(self, context):
"""
layout = self.layout

layout.prop(self, 'custom_rig_template_path')
row = layout.row()
row.operator('ue2rigify.import_rig_template', icon='IMPORT')
row.operator('ue2rigify.export_rig_template', icon='EXPORT')
Expand Down
4 changes: 4 additions & 0 deletions ue2rigify/ui/view_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def draw(self, context):
# template dropdown
row = layout.row()
row.label(text='Template:')

if properties.selected_rig_template in [
Template.DEFAULT_MALE_TEMPLATE,
Template.DEFAULT_FEMALE_TEMPLATE,
Expand All @@ -62,6 +63,9 @@ def draw(self, context):
row = layout.row()
row.prop(properties, 'selected_mode', text='')

if properties.selected_rig_template == '':
row.operator('ue2rigify.set_default')

box = layout.box()
row = box.row()
row.label(text='Rig Template Editor', icon='TOOL_SETTINGS')
Expand Down