4508 lines
200 KiB
Python
4508 lines
200 KiB
Python
# Copyright (C) 2021 Victor Soupday
|
|
# This file is part of CC/iC Blender Tools <https://github.com/soupday/cc_blender_tools>
|
|
#
|
|
# CC/iC Blender Tools 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.
|
|
#
|
|
# CC/iC Blender Tools 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 CC/iC Blender Tools. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import bpy
|
|
import textwrap
|
|
import os
|
|
|
|
from . import addon_updater_ops, iconutils, rigging, rigutils
|
|
from . import (link, rigify_mapping_data, bones, characters, sculpting, springbones,
|
|
bake, rigidbody, physics, colorspace, modifiers, channel_mixer, nodeutils,
|
|
utils, params, vars)
|
|
from .meshutils import get_head_body_object_quick
|
|
|
|
PIPELINE_TAB_NAME = "CC/iC Pipeline"
|
|
CREATE_TAB_NAME = "CC/iC Create"
|
|
LINK_TAB_NAME = "CC/iC Link"
|
|
|
|
# Panel functions and classes
|
|
#
|
|
|
|
def fake_drop_down(row, label, prop_name, prop_bool_value, icon = "TRIA_DOWN", icon_closed = "TRIA_RIGHT"):
|
|
props = vars.props()
|
|
row.alignment="LEFT"
|
|
|
|
if prop_bool_value:
|
|
row.prop(props, prop_name, icon=icon, text=label, emboss=False)
|
|
else:
|
|
row.prop(props, prop_name, icon=icon_closed, text=label, emboss=False)
|
|
|
|
if icon != "TRIA_DOWN":
|
|
if prop_bool_value:
|
|
row.prop(props, prop_name, icon="TRIA_DOWN", icon_only=True, emboss=False)
|
|
else:
|
|
row.prop(props, prop_name, icon="TRIA_RIGHT", icon_only=True, emboss=False)
|
|
return prop_bool_value
|
|
|
|
|
|
def get_layout_width(context=None, region_type="UI"):
|
|
ui_shelf = None
|
|
if not context:
|
|
context = bpy.context
|
|
area = context.area
|
|
width = 15
|
|
for region in area.regions:
|
|
if region.type == region_type:
|
|
ui_shelf = region
|
|
width = int(ui_shelf.width / 8)
|
|
return width
|
|
|
|
|
|
def wrapped_text_box(layout, info_text, width, alert = False, icon = None):
|
|
wrapper = textwrap.TextWrapper(width=width)
|
|
info_list = wrapper.wrap(info_text)
|
|
|
|
box = layout.box()
|
|
box.alert = alert
|
|
first = True
|
|
for text in info_list:
|
|
if first and icon:
|
|
box.label(text=text, icon=icon)
|
|
else:
|
|
box.label(text=text)
|
|
first = False
|
|
return box
|
|
|
|
|
|
def warn_icon(row, icon = "ERROR"):
|
|
col = row.column()
|
|
col.alert = True
|
|
col.label(text="", icon=icon)
|
|
|
|
|
|
def character_info_box(chr_cache, chr_rig, layout, show_name = True, show_type = True, show_type_selector = True):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
is_character = False
|
|
is_non_standard = True
|
|
is_morph = False
|
|
if chr_cache:
|
|
character_name = chr_cache.character_name
|
|
is_non_standard = chr_cache.is_non_standard()
|
|
if chr_cache.is_standard():
|
|
type_text = f"Standard ({chr_cache.generation})"
|
|
else:
|
|
type_text = f"Non-Standard ({chr_cache.generation})"
|
|
is_character = True
|
|
if chr_cache.is_morph():
|
|
is_morph = True
|
|
is_non_standard = False
|
|
type_text = "Obj/Morph"
|
|
elif chr_rig:
|
|
character_name = chr_rig.name
|
|
type_text = "Generic"
|
|
is_non_standard = True
|
|
is_character = True
|
|
|
|
box = layout.box()
|
|
if is_character:
|
|
if show_name:
|
|
box.row().label(text=f"Character: {character_name}")
|
|
if show_type:
|
|
box.row().label(text=f"Type: {type_text}")
|
|
if show_type_selector:
|
|
if is_non_standard:
|
|
row = box.row()
|
|
if chr_cache:
|
|
row.prop(chr_cache, "non_standard_type", expand=True)
|
|
elif chr_rig:
|
|
row.prop(prefs, "export_non_standard_mode", expand=True)
|
|
|
|
else:
|
|
box.row().label(text=f"No Character")
|
|
|
|
return box
|
|
|
|
|
|
def reconnect_character_ui(context, layout: bpy.types.UILayout, chr_cache):
|
|
width = get_layout_width(context, "UI")
|
|
rig = utils.get_context_armature(context)
|
|
if not context.selected_objects:
|
|
rig = None
|
|
wrapped_text_box(layout, "Linking", width, alert=False, icon="LINKED")
|
|
if not chr_cache and rig and rigutils.is_rigify_plus_armature(rig):
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
op = row.operator("ccic.characterlink", icon="OUTLINER_OB_ARMATURE", text="Connect Rigified").param = "CONNECT"
|
|
elif not chr_cache and rig and rigutils.is_rl_armature(rig):
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
op = row.operator("ccic.characterlink", icon="ARMATURE_DATA", text="Connect Character").param = "CONNECT"
|
|
else:
|
|
row = layout.row(align=True)
|
|
row.scale_y = 1.5
|
|
op = row.operator("ccic.characterlink", icon="LINKED", text="Link").param = "LINK"
|
|
op = row.operator("ccic.characterlink", icon="APPEND_BLEND", text="Append").param = "APPEND"
|
|
|
|
|
|
def pipeline_export_group(chr_cache, chr_rig, layout: bpy.types.UILayout):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
character_info_box(chr_cache, chr_rig, layout)
|
|
|
|
if chr_cache and chr_cache.rigified:
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(text="Export from Rigging & Animation", icon="ERROR")
|
|
|
|
# export to CC3
|
|
character_export_button(chr_cache, chr_rig, layout)
|
|
|
|
layout.separator()
|
|
|
|
# export to Unity
|
|
character_export_unity_button(chr_cache, layout)
|
|
|
|
#layout.separator()
|
|
#
|
|
## export to Unreal
|
|
# character_export_unreal_button(chr_cache, layout)
|
|
|
|
|
|
def rigify_export_group(chr_cache, layout):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
row = layout.row()
|
|
row.label(text="Include T-Pose")
|
|
row.prop(prefs, "rigify_export_t_pose", text="")
|
|
layout.label(text="Bone Naming:")
|
|
layout.prop(prefs, "rigify_export_naming", expand=True)
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
if prefs.rigify_export_mode == "MESH":
|
|
row.operator("cc3.exporter", icon="ARMATURE_DATA", text="Export Mesh").param = "EXPORT_RIGIFY"
|
|
elif prefs.rigify_export_mode == "MOTION":
|
|
row.operator("cc3.exporter", icon="ARMATURE_DATA", text="Export Motion").param = "EXPORT_RIGIFY"
|
|
else:
|
|
row.operator("cc3.exporter", icon="ARMATURE_DATA", text="Export Mesh & Motion").param = "EXPORT_RIGIFY"
|
|
layout.row().prop(prefs, "rigify_export_mode", expand=True)
|
|
|
|
|
|
def character_export_button(chr_cache, chr_rig, layout : bpy.types.UILayout, scale=2, warn=True):
|
|
# export to CC3
|
|
text = "Export to CC3/4"
|
|
icon = "MOD_ARMATURE"
|
|
|
|
standard_export = chr_cache and chr_cache.is_standard()
|
|
non_standard_export = (chr_cache and chr_cache.is_non_standard()) or (chr_rig and not standard_export)
|
|
|
|
if chr_cache:
|
|
row = layout.row()
|
|
row.scale_y = scale
|
|
param = "EXPORT_CC3"
|
|
if chr_cache and chr_cache.is_import_type("OBJ"):
|
|
text = "Export Morph Target"
|
|
icon = "ARROW_LEFTRIGHT"
|
|
if not chr_cache.is_valid_for_export():
|
|
row.alert = True
|
|
row.enabled = False
|
|
text = "Invalid Character!"
|
|
param = "EXPORT_CC3_INVALID"
|
|
row.operator("cc3.exporter", icon=icon, text=text).param = param
|
|
if not chr_cache.can_standard_export():
|
|
row.enabled = False
|
|
if warn and not chr_cache.get_import_has_key():
|
|
if chr_cache.is_import_type("FBX"):
|
|
layout.row().label(text="No Fbx-Key file!", icon="ERROR")
|
|
elif chr_cache.is_import_type("OBJ"):
|
|
layout.row().label(text="No Obj-Key file!", icon="ERROR")
|
|
|
|
|
|
elif chr_rig:
|
|
row = layout.row()
|
|
row.scale_y = scale
|
|
text = "Export Non-Standard"
|
|
icon = "MONKEY"
|
|
row.operator("cc3.exporter", icon=icon, text=text).param = "EXPORT_NON_STANDARD"
|
|
|
|
else:
|
|
row = layout.row()
|
|
row.scale_y = scale
|
|
row.operator("cc3.exporter", icon=icon, text=text).param = "EXPORT_CC3"
|
|
row.enabled = False
|
|
if warn:
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(text="No current character!", icon="ERROR")
|
|
|
|
|
|
def character_export_unity_button(chr_cache, layout):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
column = layout.column()
|
|
|
|
# export button
|
|
row = column.row()
|
|
row.scale_y = 2
|
|
if props.is_unity_project():
|
|
row.operator("cc3.exporter", icon="CUBE", text="Update Unity Project").param = "UPDATE_UNITY"
|
|
else:
|
|
row.operator("cc3.exporter", icon="CUBE", text="Export To Unity").param = "EXPORT_UNITY"
|
|
# export mode
|
|
column.row().prop(prefs, "export_unity_mode", expand=True)
|
|
|
|
# disable if no character, or not an fbx import
|
|
if not chr_cache or not (chr_cache.is_import_type("FBX") or chr_cache.is_import_type("BLEND")) or chr_cache.rigified:
|
|
column.enabled = False
|
|
|
|
|
|
def character_export_unreal_button(chr_cache, layout):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
column = layout.column()
|
|
|
|
# export button
|
|
row = column.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.exporter", icon="EVENT_U", text="Export To Unreal").param = "EXPORT_UNREAL"
|
|
|
|
# disable if no character, or not an fbx import
|
|
if not chr_cache or not chr_cache.is_import_type("FBX") or chr_cache.rigified:
|
|
column.enabled = False
|
|
|
|
|
|
def rigid_body_sim_ui(chr_cache, arm, obj, layout : bpy.types.UILayout,
|
|
fixed_parent=False, only_parent_mode=None,
|
|
show_selector=True, enabled=True):
|
|
|
|
props = vars.props()
|
|
|
|
if not chr_cache or not arm:
|
|
return
|
|
|
|
rigid_body = rigidbody.get_rigid_body(chr_cache, obj)
|
|
rigid_body_sim = None
|
|
parent_mode = chr_cache.available_spring_rigs
|
|
if fixed_parent:
|
|
parent_mode = only_parent_mode
|
|
prefix = springbones.get_spring_rig_prefix(parent_mode)
|
|
rigid_body_sim = rigidbody.get_spring_rigid_body_system(arm, prefix)
|
|
has_spring_rig = springbones.has_spring_rig(chr_cache, arm, parent_mode)
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(),
|
|
"Rigid Body Sim",
|
|
"section_rigidbody_spring_ui",
|
|
props.section_rigidbody_spring_ui,
|
|
icon="BLENDER", icon_closed="BLENDER"):
|
|
|
|
column = box.column()
|
|
column.enabled = enabled
|
|
|
|
if not fixed_parent and show_selector:
|
|
split = column.split(factor=0.45)
|
|
split.column().label(text="Hair System")
|
|
#split.column().prop(props, "hair_rig_bone_root", text="")
|
|
split.column().prop(chr_cache, "available_spring_rigs", text="")
|
|
|
|
if not has_spring_rig:
|
|
row = column.row()
|
|
row.label(text = "No Spring Rig", icon="ERROR")
|
|
|
|
if rigid_body_sim:
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Influence")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_influence\"]", text="", slider=True)
|
|
col_1.label(text="Restrain")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_limit\"]", text="", slider=True)
|
|
col_1.label(text="Curve")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_curve\"]", text="", slider=True)
|
|
col_1.label(text="Mass")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_mass\"]", text="", slider=True)
|
|
col_1.label(text="Margin")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_margin\"]", text="", slider=True)
|
|
col_1.label(text="Dampening")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_dampening\"]", text="", slider=True)
|
|
col_1.label(text="Stiffness")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_stiffness\"]", text="", slider=True)
|
|
col_1.label(text="Angle Range")
|
|
col_2.prop(rigid_body_sim, "[\"rigid_body_angle_limit\"]", text="", slider=True)
|
|
|
|
column.separator()
|
|
|
|
if parent_mode:
|
|
if not rigid_body_sim:
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
row.operator("cc3.springbones", icon=utils.check_icon("CON_KINEMATIC"), text="Build Simulation").param = "MAKE_RIGID_BODY_SYSTEM"
|
|
column.separator()
|
|
if not has_spring_rig:
|
|
row.enabled = False
|
|
else:
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
warn_icon(row, "X")
|
|
row.operator("cc3.springbones", icon=utils.check_icon("X"), text="Remove Simulation").param = "REMOVE_RIGID_BODY_SYSTEM"
|
|
column.separator()
|
|
else:
|
|
column.row().label(text = "No spring rig selected", icon="ERROR")
|
|
|
|
column.row().label(text="Rigid Body Colliders:")
|
|
|
|
if rigidbody.has_rigid_body_colliders(arm):
|
|
row = column.row(align=True)
|
|
colliders_visible = rigidbody.colliders_visible(arm)
|
|
row.operator("cc3.springbones", icon=utils.check_icon("HIDE_OFF"), text="", depress=colliders_visible).param = "TOGGLE_SHOW_COLLIDERS"
|
|
is_pose_position = rigutils.is_rig_rest_position(arm)
|
|
row.operator("ccic.rigutils", icon="OUTLINER_OB_ARMATURE", text="", depress=is_pose_position).param = "TOGGLE_SHOW_RIG_POSE"
|
|
row.operator("cc3.springbones", icon=utils.check_icon("X"), text="Remove Colliders").param = "REMOVE_COLLIDERS"
|
|
#column.row().prop(rigid_body, "collision_margin", text="Collision Margin", slider=True)
|
|
else:
|
|
column.row().operator("cc3.springbones", icon=utils.check_icon("META_CAPSULE"), text="Add Colliders").param = "BUILD_COLLIDERS"
|
|
|
|
column.separator()
|
|
|
|
# Cache
|
|
has_rigidbody, rigidbody_baked, rigidbody_baking, rigidbody_point_cache = springbones.rigidbody_state()
|
|
|
|
column.row().label(text="Animation Range:")
|
|
row = column.row(align=True)
|
|
row.prop(bpy.context.scene, "use_preview_range", text="", toggle=True)
|
|
grid = row.grid_flow(columns=2, align=True)
|
|
grid.operator("cc3.scene", icon="FULLSCREEN_ENTER", text="Expand").param = "ANIM_RANGE_EXPAND"
|
|
grid.operator("cc3.scene", icon="FULLSCREEN_EXIT", text="Fit").param = "ANIM_RANGE_FIT"
|
|
column.row().label(text="Rigid Body Cache:")
|
|
row = column.row()
|
|
row.operator("cc3.springbones", icon=utils.check_icon("LOOP_BACK"), text="Reset Simulation").param = "RESET_PHYSICS"
|
|
# frame dropping warning
|
|
if bpy.context.scene.sync_mode != "NONE":
|
|
row = column.row()
|
|
row.alert = True
|
|
row.label(text="Frame Dropping!", icon="ERROR")
|
|
#
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.context_pointer_set("point_cache", rigidbody_point_cache)
|
|
depress = rigidbody_baking
|
|
row.alert = rigidbody_baked
|
|
if rigidbody_baked:
|
|
row.operator("ptcache.free_bake", text="Free Simulation", icon="REC")
|
|
else:
|
|
row.operator("ptcache.bake", text="Bake Simulation", icon="REC", depress=rigidbody_baking).bake = True
|
|
|
|
|
|
def physics_all_dynamics_ui(layout : bpy.types.UILayout):
|
|
has_cloth, has_collision, has_rigidbody, all_baked, any_baked, all_baking, any_baking = physics.get_scene_physics_state()
|
|
layout.label(text="All Dynamics:", icon="PHYSICS")
|
|
column = layout.column(align=True)
|
|
column.operator("cc3.scene", icon="LOOP_BACK", text="Reset All").param = "PHYSICS_PREP_ALL"
|
|
# frame dropping warning
|
|
if bpy.context.scene.sync_mode != "NONE":
|
|
row = column.row(align=True)
|
|
row.alert = True
|
|
row.label(text="Frame Dropping!", icon="ERROR")
|
|
#
|
|
row = column.row(align=True)
|
|
row.scale_y = 1.5
|
|
row.alert = all_baked
|
|
all_depress = all_baking
|
|
if any_baked:
|
|
row.operator("ptcache.free_bake_all", text="Free All Dynamics", icon="REC")
|
|
else:
|
|
row.operator("ptcache.bake_all", text="Bake All Dynamics", icon="REC", depress=all_depress).bake = True
|
|
|
|
|
|
def cache_timeline_physics_ui(chr_cache, layout : bpy.types.UILayout):
|
|
if not chr_cache:
|
|
return
|
|
layout.box().label(text="Timeline & Physics Cache", icon="PREVIEW_RANGE")
|
|
layout.row().label(text="Animation Range:")
|
|
row = layout.row(align=True)
|
|
row.prop(bpy.context.scene, "use_preview_range", text="", toggle=True)
|
|
grid = row.grid_flow(columns=2, align=True)
|
|
grid.operator("cc3.scene", icon="FULLSCREEN_ENTER", text="Expand").param = "ANIM_RANGE_EXPAND"
|
|
grid.operator("cc3.scene", icon="FULLSCREEN_EXIT", text="Fit").param = "ANIM_RANGE_FIT"
|
|
|
|
layout.separator()
|
|
|
|
"""
|
|
if not bpy.data.filepath:
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(text="Blendfile should be saved", icon="ERROR")
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(text="before baking physics", icon="REMOVE")
|
|
layout.separator()
|
|
"""
|
|
|
|
if bpy.context.object:
|
|
layout.label(text=bpy.context.object.name, icon="OBJECT_DATA")
|
|
|
|
has_cloth, cloth_baked, cloth_baking, cloth_point_cache = physics.cloth_physics_state(bpy.context.object)
|
|
has_rigidbody, rigidbody_baked, rigidbody_baking, rigidbody_point_cache = springbones.rigidbody_state()
|
|
|
|
grid = layout.grid_flow(row_major=True, columns=2)
|
|
|
|
grid_column = grid.column(align=True)
|
|
grid_column.label(text="Cloth Physics", icon="MOD_CLOTH")
|
|
|
|
row = grid_column.row(align=True)
|
|
row.operator("cc3.scene", icon="LOOP_BACK", text="Reset").param = "PHYSICS_PREP_CLOTH"
|
|
# frame dropping warning
|
|
if bpy.context.scene.sync_mode != "NONE":
|
|
row = grid_column.row(align=True)
|
|
row.alert = True
|
|
row.label(text="Frame Dropping!", icon="ERROR")
|
|
#
|
|
row = grid_column.row(align=True)
|
|
row.context_pointer_set("point_cache", cloth_point_cache)
|
|
row.scale_y = 1.5
|
|
row.alert = cloth_baked
|
|
if cloth_baked:
|
|
row.operator("ptcache.free_bake", text="Free", icon="REC")
|
|
else:
|
|
row.operator("ptcache.bake", text="Bake", icon="REC", depress=cloth_baking).bake = True
|
|
|
|
if not has_cloth:
|
|
grid_column.enabled = False
|
|
|
|
grid_column = grid.column(align=True)
|
|
grid_column.label(text="Spring Physics", icon="CON_KINEMATIC")
|
|
|
|
row = grid_column.row(align=True)
|
|
row.operator("cc3.scene", icon="LOOP_BACK", text="Reset").param = "PHYSICS_PREP_RBW"
|
|
# frame dropping warning
|
|
if bpy.context.scene.sync_mode != "NONE":
|
|
row = grid_column.row(align=True)
|
|
row.alert = True
|
|
row.label(text="Frame Dropping!", icon="ERROR")
|
|
#
|
|
row = grid_column.row(align=True)
|
|
row.context_pointer_set("point_cache", rigidbody_point_cache)
|
|
row.scale_y = 1.5
|
|
row.alert = rigidbody_baked
|
|
if rigidbody_baked:
|
|
row.operator("ptcache.free_bake", text="Free", icon="REC")
|
|
else:
|
|
row.operator("ptcache.bake", text="Bake", icon="REC", depress=rigidbody_baking).bake = True
|
|
|
|
if not has_rigidbody:
|
|
grid_column.enabled = False
|
|
|
|
layout.separator()
|
|
|
|
physics_all_dynamics_ui(layout)
|
|
|
|
|
|
def character_tools_ui(context, layout: bpy.types.UILayout):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context, strict=True)
|
|
non_chr_objects = [ obj for obj in context.selected_objects
|
|
if props.get_object_cache(obj) is None
|
|
and (obj.type == "MESH"
|
|
or obj.type == "EMPTY")]
|
|
generic_rig = None
|
|
rig = None
|
|
if chr_cache:
|
|
rig = chr_cache.get_armature()
|
|
else:
|
|
generic_rig = characters.get_generic_rig(context.selected_objects)
|
|
if generic_rig:
|
|
rig = generic_rig
|
|
|
|
if chr_cache:
|
|
chr_name = chr_cache.character_name
|
|
elif generic_rig:
|
|
chr_name = generic_rig.name
|
|
elif non_chr_objects:
|
|
chr_name = non_chr_objects[0].name
|
|
else:
|
|
chr_name = "None Selected"
|
|
|
|
if chr_cache:
|
|
if chr_cache.is_non_standard():
|
|
type_string = chr_cache.non_standard_type.capitalize()
|
|
else:
|
|
type_string = chr_cache.generation.capitalize()
|
|
elif generic_rig or non_chr_objects:
|
|
type_string = "Generic"
|
|
else:
|
|
type_string = ""
|
|
|
|
box = layout.box()
|
|
if type_string:
|
|
box.label(text=f"{chr_name} ({type_string})", icon="TOOL_SETTINGS")
|
|
else:
|
|
box.label(text=f"{chr_name}", icon="TOOL_SETTINGS")
|
|
col = layout.column(align=True)
|
|
|
|
render_rebuild_ui(col, chr_cache)
|
|
|
|
grid = col.grid_flow(row_major=True, columns=2, align=True)
|
|
grid.scale_y = 1.5
|
|
grid.operator("cc3.character", icon="RESTRICT_SELECT_OFF", text="Select").param = "SELECT_ACTOR_ALL"
|
|
grid.operator("cc3.character", icon="ARMATURE_DATA", text="Select Rig").param = "SELECT_ACTOR_RIG"
|
|
row1 = grid.row(align=True)
|
|
row1.operator("ccic.rename_character", icon="GREASEPENCIL", text="Edit")
|
|
row2 = grid.row(align=True)
|
|
row2.operator("cc3.character", icon="DUPLICATE", text="Duplicate").param = "DUPLICATE"
|
|
if not chr_cache:
|
|
grid.enabled = False
|
|
if is_linked_or_override(chr_cache):
|
|
row1.enabled = False
|
|
row2.enabled = False
|
|
|
|
can_be_rigged = (chr_cache and chr_cache.is_avatar() and not chr_cache.rigified and chr_cache.can_be_rigged())
|
|
if can_be_rigged:
|
|
row = col.row(align=True)
|
|
row.row(align=True).prop(prefs, "rigify_expression_rig", expand=True)
|
|
|
|
split = col.split(factor=0.5, align=True)
|
|
col_1 = split.column(align=True)
|
|
col_2 = split.column(align=True)
|
|
col_1.scale_y = 1.5
|
|
col_2.scale_y = 1.5
|
|
col_1.operator("cc3.rigifier", icon="OUTLINER_OB_ARMATURE", text="Rigify").param = "DATALINK_RIGIFY"
|
|
if not (chr_cache and chr_cache.is_avatar() and not chr_cache.rigified and chr_cache.can_be_rigged()):
|
|
col_1.enabled = False
|
|
if chr_cache:
|
|
col_2.operator("cc3.importer", icon="PANEL_CLOSE", text="Delete").param ="DELETE_CHARACTER"
|
|
elif generic_rig or non_chr_objects:
|
|
if generic_rig:
|
|
col_2.operator("ccic.convert_generic", icon="COMMUNITY", text="Convert")
|
|
elif non_chr_objects:
|
|
col_2.operator("ccic.convert_generic", icon="OUTLINER_OB_META", text="Convert")
|
|
else:
|
|
col_2.operator("cc3.importer", icon="PANEL_CLOSE", text="Delete").param ="DELETE_CHARACTER"
|
|
col_2.enabled = False
|
|
|
|
if chr_cache or generic_rig or non_chr_objects:
|
|
if chr_cache:
|
|
if chr_cache.link_id:
|
|
row = layout.row(align=True)
|
|
row.label(text=f"Link ID: {chr_cache.link_id}")
|
|
if chr_cache.import_file:
|
|
row.operator("ccic.datalink", icon="FILE_FOLDER", text="").param = "SHOW_ACTOR_FILES"
|
|
row.operator("ccic.datalink", icon="OPTIONS", text="").param = "SHOW_ACTOR_JSON"
|
|
else:
|
|
layout.row().label(text=f"{type_string}: Unlinked")
|
|
elif generic_rig:
|
|
layout.row().label(text=f"Armature: {generic_rig.name}")
|
|
elif non_chr_objects:
|
|
obj_text = "Objects: "
|
|
for i, obj in enumerate(non_chr_objects):
|
|
if i > 0:
|
|
obj_text += ", "
|
|
obj_text += obj.name
|
|
if len(non_chr_objects) == 0:
|
|
obj_text += "None"
|
|
layout.row().label(text=obj_text)
|
|
|
|
|
|
def render_rebuild_ui(layout: bpy.types.UILayout, chr_cache):
|
|
if chr_cache and chr_cache.setup_mode == "ADVANCED":
|
|
width = get_layout_width(None, "UI")
|
|
# Cycles Prefs
|
|
if bpy.context.scene.render.engine != "BLENDER_WORKBENCH":
|
|
engine_render_target = "CYCLES" if bpy.context.scene.render.engine == "CYCLES" else "EEVEE"
|
|
if chr_cache.get_render_target() != engine_render_target:
|
|
box = layout.box()
|
|
box.alert = True
|
|
row = box.row(align=True)
|
|
row.scale_y = 2
|
|
text = "Rebuild for " + engine_render_target
|
|
row.operator("cc3.importer", icon="SHADING_TEXTURE", text=text).param ="BUILD_REBUILD"
|
|
|
|
|
|
|
|
def render_prefs_ui(layout: bpy.types.UILayout, index=1):
|
|
prefs = vars.prefs()
|
|
props = vars.props()
|
|
|
|
# Cycles Prefs
|
|
if bpy.context.scene.render.engine == "CYCLES":
|
|
suffix = "B4.1" if utils.B410() else "B3.4"
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(),
|
|
f"Cycles Prefs ({suffix})",
|
|
f"cycles_options{index}",
|
|
props.cycles_options1 if index==1 else props.cycles_options2):
|
|
column = box.column()
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
if utils.B400():
|
|
col_1.label(text = "Iris Brightness")
|
|
col_2.prop(prefs, "cycles_iris_brightness_b443b", text = "")
|
|
col_1.label(text = "Skin SSS")
|
|
col_2.prop(prefs, "cycles_sss_skin_b443b", text = "")
|
|
col_1.label(text = "Hair SSS")
|
|
col_2.prop(prefs, "cycles_sss_hair_b443b", text = "")
|
|
col_1.label(text = "Teeth SSS")
|
|
col_2.prop(prefs, "cycles_sss_teeth_b443b", text = "")
|
|
col_1.label(text = "Tongue SSS")
|
|
col_2.prop(prefs, "cycles_sss_tongue_b443b", text = "")
|
|
col_1.label(text = "Eyes SSS")
|
|
col_2.prop(prefs, "cycles_sss_eyes_b443b", text = "")
|
|
col_1.label(text = "Default SSS")
|
|
col_2.prop(prefs, "cycles_sss_default_b443b", text = "")
|
|
col_1.label(text = "Roughness Power")
|
|
col_2.prop(prefs, "cycles_roughness_power_b443b", text = "")
|
|
col_1.label(text = "Normal Strength")
|
|
col_2.prop(prefs, "cycles_normal_b443b", text = "")
|
|
col_1.label(text = "Skin Normal Strength")
|
|
col_2.prop(prefs, "cycles_normal_skin_b443b", text = "")
|
|
col_1.label(text = "Micro Normal Strength")
|
|
col_2.prop(prefs, "cycles_micro_normal_b443b", text = "")
|
|
else:
|
|
col_1.label(text = "Iris Brightness")
|
|
col_2.prop(prefs, "cycles_iris_brightness_b443b", text = "")
|
|
col_1.label(text = "Skin SSS")
|
|
col_2.prop(prefs, "cycles_sss_skin_b341", text = "")
|
|
col_1.label(text = "Hair SSS")
|
|
col_2.prop(prefs, "cycles_sss_hair_b341", text = "")
|
|
col_1.label(text = "Teeth SSS")
|
|
col_2.prop(prefs, "cycles_sss_teeth_b341", text = "")
|
|
col_1.label(text = "Tongue SSS")
|
|
col_2.prop(prefs, "cycles_sss_tongue_b341", text = "")
|
|
col_1.label(text = "Eyes SSS")
|
|
col_2.prop(prefs, "cycles_sss_eyes_b341", text = "")
|
|
col_1.label(text = "Default SSS")
|
|
col_2.prop(prefs, "cycles_sss_default_b341", text = "")
|
|
col_1.label(text = "Roughness Power")
|
|
col_2.prop(prefs, "cycles_roughness_power_b341", text = "")
|
|
col_1.label(text = "Normal Strength")
|
|
col_2.prop(prefs, "cycles_normal_b341", text = "")
|
|
col_1.label(text = "Skin Normal Strength")
|
|
col_2.prop(prefs, "cycles_normal_skin_b341", text = "")
|
|
col_1.label(text = "Micro Normal Strength")
|
|
col_2.prop(prefs, "cycles_micro_normal_b341", text = "")
|
|
col_1.operator("cc3.setpreferences", icon="FILE_REFRESH", text="Reset").param="RESET_CYCLES"
|
|
col_2.operator("cc3.setproperties", icon="DECORATE_DRIVER", text="Update").param = "APPLY_ALL"
|
|
|
|
# Eevee Prefs
|
|
else:
|
|
suffix = "B4.2" if utils.B420() else "B3.4"
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(),
|
|
f"Eevee Prefs ({suffix})",
|
|
f"eevee_options{index}",
|
|
props.eevee_options1 if index==1 else props.eevee_options2):
|
|
column = box.column()
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
if utils.B420():
|
|
col_1.label(text = "Iris Brightness")
|
|
col_2.prop(prefs, "eevee_iris_brightness_b443b", text = "")
|
|
col_1.label(text = "Skin SSS")
|
|
col_2.prop(prefs, "eevee_sss_skin_b443b", text = "")
|
|
col_1.label(text = "Hair SSS")
|
|
col_2.prop(prefs, "eevee_sss_hair_b443b", text = "")
|
|
col_1.label(text = "Teeth SSS")
|
|
col_2.prop(prefs, "eevee_sss_teeth_b443b", text = "")
|
|
col_1.label(text = "Tongue SSS")
|
|
col_2.prop(prefs, "eevee_sss_tongue_b443b", text = "")
|
|
col_1.label(text = "Eyes SSS")
|
|
col_2.prop(prefs, "eevee_sss_eyes_b443b", text = "")
|
|
col_1.label(text = "Default SSS")
|
|
col_2.prop(prefs, "eevee_sss_default_b443b", text = "")
|
|
col_1.label(text = "Roughness Power")
|
|
col_2.prop(prefs, "eevee_roughness_power_b443b", text = "")
|
|
col_1.label(text = "Normal Strength")
|
|
col_2.prop(prefs, "eevee_normal_b443b", text = "")
|
|
col_1.label(text = "Skin Normal Strength")
|
|
col_2.prop(prefs, "eevee_normal_skin_b443b", text = "")
|
|
col_1.label(text = "Micro Normal Strength")
|
|
col_2.prop(prefs, "eevee_micro_normal_b443b", text = "")
|
|
else:
|
|
col_1.label(text = "Iris Brightness")
|
|
col_2.prop(prefs, "eevee_iris_brightness_b443b", text = "")
|
|
col_1.label(text = "Skin SSS")
|
|
col_2.prop(prefs, "eevee_sss_skin_b341", text = "")
|
|
col_1.label(text = "Hair SSS")
|
|
col_2.prop(prefs, "eevee_sss_hair_b341", text = "")
|
|
col_1.label(text = "Teeth SSS")
|
|
col_2.prop(prefs, "eevee_sss_teeth_b341", text = "")
|
|
col_1.label(text = "Tongue SSS")
|
|
col_2.prop(prefs, "eevee_sss_tongue_b341", text = "")
|
|
col_1.label(text = "Eyes SSS")
|
|
col_2.prop(prefs, "eevee_sss_eyes_b341", text = "")
|
|
col_1.label(text = "Default SSS")
|
|
col_2.prop(prefs, "eevee_sss_default_b341", text = "")
|
|
col_1.label(text = "Roughness Power")
|
|
col_2.prop(prefs, "eevee_roughness_power_b341", text = "")
|
|
col_1.label(text = "Normal Strength")
|
|
col_2.prop(prefs, "eevee_normal_b341", text = "")
|
|
col_1.label(text = "Skin Normal Strength")
|
|
col_2.prop(prefs, "eevee_normal_skin_b341", text = "")
|
|
col_1.label(text = "Micro Normal Strength")
|
|
col_2.prop(prefs, "eevee_micro_normal_b341", text = "")
|
|
col_1.operator("cc3.setpreferences", icon="FILE_REFRESH", text="Reset").param="RESET_EEVEE"
|
|
col_2.operator("cc3.setproperties", icon="DECORATE_DRIVER", text="Update").param = "APPLY_ALL"
|
|
|
|
|
|
def is_linked_or_override(chr_cache):
|
|
linked_or_override = False
|
|
if chr_cache:
|
|
arm = chr_cache.get_armature()
|
|
if arm:
|
|
linked_or_override = utils.obj_is_override(arm) or utils.obj_is_linked(arm)
|
|
return linked_or_override
|
|
|
|
|
|
def disable_on_linked(layout, chr_cache):
|
|
layout.enabled = not is_linked_or_override(chr_cache)
|
|
|
|
|
|
class ARMATURE_UL_List(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
|
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
|
layout.label(text=item.name if item else "", translate=False, icon_value=icon)
|
|
elif self.layout_type in {'GRID'}:
|
|
layout.alignment = 'CENTER'
|
|
layout.label(text="", icon_value=icon)
|
|
|
|
def filter_items(self, context, data, propname):
|
|
filtered = []
|
|
ordered = []
|
|
items = getattr(data, propname)
|
|
filtered = [self.bitflag_filter_item] * len(items)
|
|
for i, item in enumerate(items):
|
|
item_name = utils.strip_name(item.name)
|
|
allowed = False
|
|
if item.type == "ARMATURE": # only list armatures
|
|
set_generation = rigutils.get_set_generation(item)
|
|
if set_generation in ["G3", "GameBase", "Mixamo"]:
|
|
allowed = True
|
|
elif set_generation in ["Rigify", "Rigify+"]:
|
|
allowed = False
|
|
elif ("_Rigify" not in item_name and # don't list rigified armatures
|
|
"_Retarget" not in item_name and # don't list retarget armatures
|
|
"_metarig" not in item_name and
|
|
len(item.data.bones) > 0):
|
|
for allowed_bone in rigify_mapping_data.ALLOWED_RIG_BONES: # only list armatures of the allowed sources
|
|
if rigutils.bone_name_in_armature_regex(item, allowed_bone):
|
|
allowed = True
|
|
# filter by name
|
|
if allowed and self.filter_name and self.filter_name != "*":
|
|
if self.filter_name not in item.name:
|
|
allowed = False
|
|
# block not allowed
|
|
if not allowed:
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
return filtered, ordered
|
|
|
|
|
|
class ACTION_UL_List(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
|
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
|
layout.label(text=item.name if item else "", translate=False, icon_value=icon)
|
|
elif self.layout_type in {'GRID'}:
|
|
layout.alignment = 'CENTER'
|
|
layout.label(text="", icon_value=icon)
|
|
|
|
def filter_items(self, context, data, propname):
|
|
props = vars.props()
|
|
filtered = []
|
|
ordered = []
|
|
arm_name = None
|
|
arm_object = utils.collection_at_index(props.armature_list_index, bpy.data.objects)
|
|
arm_set_generation = None
|
|
if arm_object:
|
|
if arm_object.type == "ARMATURE":
|
|
arm_name = arm_object.name
|
|
arm_set_generation = rigutils.get_set_generation(arm_object)
|
|
rl_arm_id = utils.get_rl_object_id(arm_object)
|
|
items = getattr(data, propname)
|
|
filtered = [self.bitflag_filter_item] * len(items)
|
|
item : bpy.types.Action
|
|
for i, item in enumerate(items):
|
|
allowed = False
|
|
action_set_generation = rigutils.get_set_generation(item)
|
|
action_type = utils.get_prop(item, "rl_action_type")
|
|
action_armature_id = utils.get_prop(item, "rl_armature_id")
|
|
channel = utils.get_action_channelbag(item, slot_type="OBJECT")
|
|
if props.armature_action_filter and arm_object:
|
|
if arm_set_generation and action_set_generation and action_type and rl_arm_id and action_armature_id:
|
|
if (arm_set_generation == action_set_generation and
|
|
(action_type == "ARM" or action_type == "SLOTTED") and
|
|
action_armature_id == rl_arm_id):
|
|
allowed = True
|
|
elif channel and len(channel.fcurves) > 0:
|
|
if channel.fcurves[0].data_path.startswith("key_blocks"):
|
|
# no shape key actions
|
|
allowed = False
|
|
else:
|
|
# only actions with curves
|
|
allowed = True
|
|
# filter by name
|
|
if allowed and self.filter_name and self.filter_name != "*":
|
|
if self.filter_name not in item.name:
|
|
allowed = False
|
|
# block not allowed
|
|
if not allowed:
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
return filtered, ordered
|
|
|
|
|
|
class ACTION_SET_UL_List(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
|
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
|
layout.label(text=item.name if item else "", translate=False, icon_value=icon)
|
|
elif self.layout_type in {'GRID'}:
|
|
layout.alignment = 'CENTER'
|
|
layout.label(text="", icon_value=icon)
|
|
|
|
def filter_items(self, context, data, propname):
|
|
props = vars.props()
|
|
filtered = []
|
|
ordered = []
|
|
items = getattr(data, propname)
|
|
filtered = [self.bitflag_filter_item] * len(items)
|
|
item : bpy.types.Action
|
|
chr_cache = props.get_context_character_cache(context)
|
|
arm_set_generation = None
|
|
arm_rig_id = None
|
|
if chr_cache:
|
|
arm = chr_cache.get_armature()
|
|
arm_set_generation = rigutils.get_set_generation(arm)
|
|
arm_rig_id = utils.get_prop(arm, "rl_armature_id")
|
|
for i, item in enumerate(items):
|
|
allowed = False
|
|
action_set_generation = rigutils.get_set_generation(item)
|
|
action_type = utils.get_prop(item, "rl_action_type")
|
|
action_rig_id = utils.get_prop(item, "rl_armature_id")
|
|
if action_type == "ARM" or action_type == "SLOTTED":
|
|
if props.filter_motion_set_type == "RIG":
|
|
allowed = arm_rig_id and action_rig_id and arm_rig_id == action_rig_id
|
|
elif props.filter_motion_set_type == "GEN":
|
|
allowed = arm_set_generation and action_set_generation and arm_set_generation == action_set_generation
|
|
else:
|
|
allowed = True
|
|
# filter by name
|
|
if allowed and self.filter_name and self.filter_name != "*":
|
|
if self.filter_name not in item.name:
|
|
allowed = False
|
|
# block not allowed
|
|
if not allowed:
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
return filtered, ordered
|
|
|
|
|
|
class UNITY_ACTION_UL_List(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
|
if self.layout_type in {'DEFAULT', 'COMPACT'}:
|
|
layout.label(text=item.name if item else "", translate=False, icon_value=icon)
|
|
elif self.layout_type in {'GRID'}:
|
|
layout.alignment = 'CENTER'
|
|
layout.label(text="", icon_value=icon)
|
|
|
|
def filter_items(self, context, data, propname):
|
|
filtered = []
|
|
ordered = []
|
|
items = getattr(data, propname)
|
|
filtered = [self.bitflag_filter_item] * len(items)
|
|
item : bpy.types.Action
|
|
for i, item in enumerate(items):
|
|
if "_Unity" in item.name and "|A|" in item.name:
|
|
channel = utils.get_action_channelbag(item, slot_type="OBJECT")
|
|
if channel and len(channel.fcurves) == 0: # no fcurves, no animation...
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
elif channel and channel.fcurves[0].data_path.startswith("key_blocks"): # only shapekey actions have key blocks...
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
if self.filter_name and self.filter_name != "*":
|
|
if self.filter_name not in item.name:
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
else:
|
|
filtered[i] &= ~self.bitflag_filter_item
|
|
return filtered, ordered
|
|
|
|
|
|
class CC3CharacterSettingsPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Character_Settings_Panel"
|
|
bl_label = "Character Build Settings"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
props = vars.props()
|
|
PREFS = vars.prefs()
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
mesh_in_selection = False
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
mesh_in_selection = True
|
|
|
|
box = layout.box()
|
|
#op = box.operator("cc3.importer", icon="IMPORT", text="Import Character")
|
|
#op.param ="IMPORT"
|
|
# import details
|
|
if chr_cache:
|
|
if fake_drop_down(box.row(), "Import Details", "stage1_details", props.stage1_details):
|
|
box.label(text="Name: " + chr_cache.character_name)
|
|
box.label(text="Type: " + chr_cache.get_import_type().upper())
|
|
split = box.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Generation:")
|
|
col_2.prop(chr_cache, "generation", text="")
|
|
col_1.label(text="Key File:")
|
|
has_key = "Yes" if chr_cache.get_import_has_key() else "No"
|
|
col_2.label(text=has_key)
|
|
col_1.label(text="Render For:")
|
|
col_2.prop(chr_cache, "render_target", text="")
|
|
box.prop(chr_cache, "import_file", text="")
|
|
for obj_cache in chr_cache.object_cache:
|
|
#o = obj_cache.get_object()
|
|
#if o:
|
|
box.prop(obj_cache, "object", text="")
|
|
else:
|
|
box.label(text="Name: " + chr_cache.character_name)
|
|
else:
|
|
box.label(text="No Character")
|
|
disable_on_linked(box, chr_cache)
|
|
|
|
# Build Settings
|
|
|
|
# Build prefs in title
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(), "Build Settings", "show_build_prefs2", props.show_build_prefs2,
|
|
icon="TOOL_SETTINGS", icon_closed="TOOL_SETTINGS"):
|
|
column = box.column()
|
|
column.prop(PREFS, "import_deduplicate")
|
|
column.prop(PREFS, "import_auto_convert")
|
|
column.prop(PREFS, "import_reset_custom_normals")
|
|
column.prop(PREFS, "clean_empty_mesh_data")
|
|
column.prop(PREFS, "action_use_action_slots")
|
|
column.prop(PREFS, "action_add_key_slots_per_obj")
|
|
#column.prop(PREFS, "action_clean_actions")
|
|
column.prop(PREFS, "action_add_empty_key_channels")
|
|
column.separator()
|
|
if PREFS.import_auto_convert:
|
|
column.prop(PREFS, "auto_convert_materials")
|
|
column.prop(PREFS, "build_limit_textures")
|
|
column.prop(PREFS, "build_pack_texture_channels")
|
|
column.prop(PREFS, "build_reuse_baked_channel_packs")
|
|
column.prop(PREFS, "build_armature_edit_modifier")
|
|
column.prop(PREFS, "build_armature_preserve_volume")
|
|
column.separator()
|
|
column.label(text="Drivers:")
|
|
column.prop(PREFS, "build_shape_key_bone_drivers_jaw")
|
|
column.prop(PREFS, "build_shape_key_bone_drivers_eyes")
|
|
column.prop(PREFS, "build_shape_key_bone_drivers_head")
|
|
column.prop(PREFS, "build_body_key_drivers")
|
|
column.separator()
|
|
column.label(text="Max Texture Sizes:")
|
|
column.prop(PREFS, "use_max_tex_size")
|
|
if PREFS.use_max_tex_size:
|
|
column.prop(PREFS, "size_max_tex_default")
|
|
column.prop(PREFS, "size_max_tex_detail")
|
|
column.prop(PREFS, "size_max_tex_minimal")
|
|
|
|
column = layout.column(align=True)
|
|
row = column.row(align=True)
|
|
row.prop(props, "physics_mode", toggle=True, text="Build Physics")
|
|
row.prop(props, "wrinkle_mode", toggle=True, text="Wrinkles")
|
|
row = column.row(align=True)
|
|
var = chr_cache if chr_cache else props
|
|
row.prop(var, "setup_mode", expand=True)
|
|
if var.setup_mode == "ADVANCED":
|
|
row = column.row(align=True)
|
|
row.prop(PREFS, "refractive_eyes", expand=True)
|
|
|
|
# ACES Prefs
|
|
if colorspace.is_aces():
|
|
layout.box().label(text="ACES Settings", icon="COLOR")
|
|
split = layout.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="sRGB Override")
|
|
col_2.prop(PREFS, "aces_srgb_override", text="")
|
|
col_1.label(text="Data Override")
|
|
col_2.prop(PREFS, "aces_data_override", text="")
|
|
|
|
render_prefs_ui(layout, 1)
|
|
|
|
# Build Button
|
|
if chr_cache:
|
|
box = layout.box()
|
|
box.row().label(text="Rebuild", icon="MOD_BUILD")
|
|
|
|
col = box.column()
|
|
row = col.row()
|
|
row.scale_y = 2
|
|
if chr_cache.setup_mode == "ADVANCED":
|
|
engine_render_target = "CYCLES" if bpy.context.scene.render.engine == "CYCLES" else "EEVEE"
|
|
if chr_cache.get_render_target() != engine_render_target:
|
|
row.alert = True
|
|
op = row.operator("cc3.importer", icon="SHADING_TEXTURE", text="Rebuild Materials").param ="BUILD_REBUILD"
|
|
else:
|
|
op = row.operator("cc3.importer", icon="SHADING_TEXTURE", text="Rebuild Materials").param ="BUILD"
|
|
else:
|
|
op = row.operator("cc3.importer", icon="NODE_MATERIAL", text="Rebuild Materials").param ="BUILD"
|
|
|
|
row = col.row()
|
|
row.scale_y = 1
|
|
op = row.operator("cc3.importer", icon="DRIVER", text="Rebuild Drivers").param ="BUILD_DRIVERS"
|
|
|
|
row = col.row()
|
|
row.scale_y = 1
|
|
op = row.operator("cc3.importer", icon="X", text="Remove Drivers").param ="REMOVE_DRIVERS"
|
|
|
|
row = box.row()
|
|
row.prop(props, "build_mode", expand=True)
|
|
box.row().operator("cc3.setproperties", icon="DECORATE_OVERRIDE", text="Reset All Parameters").param = "RESET_ALL"
|
|
row = box.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.importer", icon="MOD_BUILD", text="Rebuild Shaders").param ="REBUILD_NODE_GROUPS"
|
|
disable_on_linked(box, chr_cache)
|
|
|
|
# Material Setup
|
|
layout.box().label(text="Object & Material Setup", icon="MATERIAL")
|
|
column = layout.column()
|
|
if not mesh_in_selection:
|
|
column.enabled = False
|
|
if obj is not None:
|
|
column.template_list("MATERIAL_UL_weightedmatslots", "", obj, "material_slots", obj, "active_material_index", rows=1)
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
|
|
if chr_cache and obj_cache:
|
|
if obj is not None:
|
|
obj_cache = chr_cache.get_object_cache(obj)
|
|
if obj_cache is not None:
|
|
col_1.label(text="Object Type")
|
|
col_2.prop(obj_cache, "object_type", text = "")
|
|
if mat is not None:
|
|
mat_cache = chr_cache.get_material_cache(mat)
|
|
if mat_cache is not None:
|
|
col_1.label(text="Material Type")
|
|
col_2.prop(mat_cache, "material_type", text = "")
|
|
|
|
|
|
col_1.label(text="Set By:")
|
|
col_1.prop(props, "quick_set_mode", expand=True)
|
|
col_1.label(text="")
|
|
op = col_2.operator("cc3.setmaterials", icon="SHADING_SOLID", text="Opaque")
|
|
op.param = "OPAQUE"
|
|
op = col_2.operator("cc3.setmaterials", icon="SHADING_WIRE", text="Blend")
|
|
op.param = "BLEND"
|
|
op = col_2.operator("cc3.setmaterials", icon="SHADING_RENDERED", text="Hashed")
|
|
op.param = "HASHED"
|
|
op = col_2.operator("cc3.setmaterials", icon="SHADING_TEXTURE", text="Clipped")
|
|
op.param = "CLIP"
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.separator()
|
|
col_2.separator()
|
|
op = col_1.operator("cc3.setmaterials", icon="NORMALS_FACE", text="Single Sided")
|
|
op.param = "SINGLE_SIDED"
|
|
op = col_2.operator("cc3.setmaterials", icon="XRAY", text="Double Sided")
|
|
op.param = "DOUBLE_SIDED"
|
|
disable_on_linked(column, chr_cache)
|
|
|
|
|
|
class CC3ObjectManagementPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Object_Management_Panel"
|
|
bl_label = "Character Management"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
generic_rig = None
|
|
arm = None
|
|
if chr_cache:
|
|
arm = chr_cache.get_armature()
|
|
else:
|
|
generic_rig = characters.get_generic_rig(context.selected_objects)
|
|
if generic_rig:
|
|
arm = generic_rig
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
rigified = chr_cache and chr_cache.rigified
|
|
is_standard = chr_cache and chr_cache.is_standard()
|
|
num_meshes_in_selection = 0
|
|
removable_objects = False
|
|
missing_materials = False
|
|
objects_addable = False
|
|
is_character = chr_cache is not None
|
|
from_other_character = False
|
|
for o in bpy.context.selected_objects:
|
|
if o.type == "MESH":
|
|
num_meshes_in_selection += 1
|
|
if chr_cache:
|
|
oc = chr_cache.get_object_cache(o)
|
|
if oc and not oc.disabled:
|
|
if oc.object_type == "DEFAULT" or oc.object_type == "HAIR":
|
|
removable_objects = True
|
|
if not chr_cache.has_all_materials(o.data.materials):
|
|
missing_materials = True
|
|
if not oc or oc.disabled:
|
|
objects_addable = True
|
|
if not from_other_character:
|
|
cc = props.get_character_cache(o, None)
|
|
if cc is not None and cc != chr_cache:
|
|
from_other_character = True
|
|
|
|
column = layout.column()
|
|
|
|
# Character Tools
|
|
character_tools_ui(context, column)
|
|
|
|
# Accessory Management
|
|
|
|
if not rigified and is_standard:
|
|
|
|
column.box().label(text="Accessories", icon="GROUP_BONE")
|
|
|
|
accessory_root = characters.get_accessory_root(chr_cache, obj)
|
|
if accessory_root:
|
|
|
|
#column.box().label(text = f"Accessory: {accessory_root.name}")
|
|
box = column.box()
|
|
split = box.split(factor=0.375)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Accessory:")
|
|
col_2.prop(accessory_root, "name", text="")
|
|
col_1.label(text="Parent:")
|
|
col_2.prop(accessory_root, "parent", text="")
|
|
else:
|
|
split = None
|
|
if chr_cache and arm:
|
|
split = column.split(factor=0.375)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Parent:")
|
|
col_2.prop_search(chr_cache, "accessory_parent_bone", arm.data, "bones", text="")
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="CONSTRAINT_BONE", text="Convert to Accessory").param = "CONVERT_ACCESSORY"
|
|
if not chr_cache or not obj or obj.type != "MESH" or (obj_cache and obj_cache.object_type == "BODY"):
|
|
row.enabled = False
|
|
if split:
|
|
split.enabled = False
|
|
|
|
column.separator()
|
|
|
|
# Checking
|
|
|
|
column.box().label(text="Checking", icon="SHADERFX")
|
|
|
|
row = column.row()
|
|
row.operator("cc3.exporter", icon="CHECKMARK", text="Check Export").param = "CHECK_EXPORT"
|
|
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="REMOVE", text="Clean Up Data").param = "CLEAN_UP_DATA"
|
|
if not is_character:
|
|
row.enabled = False
|
|
|
|
column.separator()
|
|
|
|
# Export Settings
|
|
|
|
column.box().label(text="Export Settings", icon="EXPORT")
|
|
|
|
split = column.split(factor=0.5)
|
|
split.column().label(text = "Texture Size")
|
|
split.column().prop(prefs, "export_texture_size", text = "")
|
|
|
|
column.separator()
|
|
|
|
# Objects & Materials
|
|
|
|
column.box().label(text="Objects & Materials", icon="OBJECT_HIDDEN")
|
|
|
|
if from_other_character:
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="PASTEDOWN", text="Copy to Character").param = "COPY_TO_CHARACTER"
|
|
if not objects_addable:
|
|
row.enabled = False
|
|
else:
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="ADD", text="Add to Character").param = "ADD_PBR"
|
|
if not objects_addable:
|
|
row.enabled = False
|
|
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="REMOVE", text="Remove from Character").param = "REMOVE_OBJECT"
|
|
if not removable_objects:
|
|
row.enabled = False
|
|
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="ADD", text="Add New Materials").param = "ADD_MATERIALS"
|
|
if not missing_materials:
|
|
row.enabled = False
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="MATERIAL", text="Match Materials").param = "MATCH_MATERIALS"
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="KEY_DEHLT", text="Clean Empty Data").param = "CLEAN_SHAPE_KEYS"
|
|
|
|
|
|
class CC3WeightPaintPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Weight_Paint_Panel"
|
|
bl_label = "Weight Painting"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
generic_rig = None
|
|
arm = None
|
|
if chr_cache:
|
|
arm = chr_cache.get_armature()
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
weight_transferable = False
|
|
if chr_cache:
|
|
for o in bpy.context.selected_objects:
|
|
if utils.object_exists_is_mesh(o):
|
|
oc = chr_cache.get_object_cache(o)
|
|
if oc and not oc.disabled:
|
|
if oc.object_type != "BODY":
|
|
weight_transferable = True
|
|
|
|
# Armature & Weights
|
|
|
|
column = layout.column()
|
|
column.box().label(text = "Armature & Weights", icon = "ARMATURE_DATA")
|
|
|
|
if arm:
|
|
column.row().prop(arm.data, "pose_position", expand=True)
|
|
|
|
column.row().label(text="Surface Copy")
|
|
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.character", icon="MOD_DATA_TRANSFER", text="Transfer Weights").param = "TRANSFER_WEIGHTS"
|
|
if not weight_transferable:
|
|
row.enabled = False
|
|
|
|
if rigging.is_surface_heat_voxel_skinning_installed():
|
|
|
|
column = layout.column()
|
|
column.row().label(text="Voxel Skinning")
|
|
|
|
# bpy.data.scenes["Scene"].voxel_resolution
|
|
# bpy.data.scenes["Scene"].voxel_falloff
|
|
column.prop(bpy.context.scene, "voxel_resolution", slider=True)
|
|
column.prop(bpy.context.scene, "voxel_falloff", slider=True)
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.rigifier_modal", icon="COMMUNITY", text="Voxel Diffuse Skinning").param = "VOXEL_HEAT_SKINNING"
|
|
row.enabled = chr_cache is not None and obj is not None and obj.type == "MESH"
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Smoothing")
|
|
|
|
column.row().operator("cc3.character", icon="SMOOTHCURVE", text="Light Smooth").param = "WEIGHTS_LIGHT_SMOOTH"
|
|
column.row().operator("cc3.character", icon="SPHERECURVE", text="Heavy Smooth").param = "WEIGHTS_HEAVY_SMOOTH"
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Surface Correction Blend")
|
|
|
|
column = layout.column(align=True)
|
|
row = column.row(align=True)
|
|
row.scale_y = 1.5
|
|
#row.operator("cc3.character", icon="IPO_BEZIER", text="Blend Weights").param = "BLEND_WEIGHTS"
|
|
#row.operator("ccic.weight_transfer", icon="ANIM", text="")
|
|
row.operator("ccic.weight_transfer", icon="IPO_BEZIER", text="Blend Body Weights")
|
|
grid = column.grid_flow(columns=2, align=True, row_major=True)
|
|
grid.prop(prefs, "weight_blend_use_range", toggle=True)
|
|
grid.prop(prefs, "weight_blend_selected_only", toggle=True)
|
|
grid.prop(prefs, "weight_blend_distance_min", text="", slider=True)
|
|
if prefs.weight_blend_use_range:
|
|
grid.prop(prefs, "weight_blend_distance_range", text="", slider=True)
|
|
else:
|
|
grid.prop(prefs, "weight_blend_distance_max", text="", slider=True)
|
|
if not weight_transferable:
|
|
column.enabled = False
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Tools")
|
|
|
|
column = layout.column()
|
|
row = column.row()
|
|
row.operator("cc3.character", icon="ORIENTATION_NORMAL", text="Normalize Weights").param = "NORMALIZE_WEIGHTS"
|
|
if not weight_transferable:
|
|
row.enabled = False
|
|
|
|
|
|
|
|
class CC3SpringRigPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_SpringRig_Panel"
|
|
bl_label = "Spring Rigging"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
arm = None
|
|
can_hair_spring_rig = False
|
|
can_spring_rig = False
|
|
has_spring_rig = False
|
|
if chr_cache:
|
|
arm = chr_cache.get_armature()
|
|
if arm:
|
|
can_spring_rig = True
|
|
can_hair_spring_rig = chr_cache.can_hair_spring_rig()
|
|
parent_mode = chr_cache.available_spring_rigs
|
|
has_spring_rig = springbones.has_spring_rig(chr_cache, arm, parent_mode)
|
|
|
|
if chr_cache and not can_hair_spring_rig:
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(icon="ERROR", text="Unsupported Character")
|
|
elif not chr_cache:
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(icon="ERROR", text="Invalid Character")
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
# Hair Cards & Spring Bone Rig
|
|
|
|
icon = utils.check_icon("OUTLINER_OB_HAIR")
|
|
if fake_drop_down(layout.box().row(),
|
|
"Hair Rigging",
|
|
"section_hair_rigging",
|
|
props.section_hair_rigging,
|
|
icon=icon, icon_closed=icon):
|
|
|
|
edit_enabled = True
|
|
# don't allow spring rig editing if has a control rig
|
|
if (chr_cache and chr_cache.rigified and
|
|
springbones.is_rigified(chr_cache, arm, props.hair_rig_bone_root)):
|
|
edit_enabled = False
|
|
|
|
valid_character = False
|
|
if chr_cache and chr_cache.can_hair_spring_rig():
|
|
valid_character = True
|
|
|
|
column = layout.column()
|
|
column.enabled = valid_character
|
|
column.label(text="Hair Card UV Directions:", icon="OUTLINER_OB_LATTICE")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Vertical Cards")
|
|
col_2.row().prop(props, "hair_card_vertical_dir", text="", expand=True)
|
|
col_1.label(text="Horizontal Cards")
|
|
col_2.row().prop(props, "hair_card_horizontal_dir", text="", expand=True)
|
|
col_1.label(text="Square Cards")
|
|
col_2.row().prop(props, "hair_card_square_dir", text="", expand=True)
|
|
column.row().prop(props, "hair_card_dir_threshold", text="Alignment Threshold", slider=True)
|
|
|
|
column.separator()
|
|
|
|
split = column.split(factor=0.45)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Hair System")
|
|
col_2.prop(props, "hair_rig_bone_root", text="")
|
|
|
|
box = column.box()
|
|
box.label(text="Hair Spring Rig", icon="FORCE_MAGNETIC")
|
|
row = box.row()
|
|
row.prop(props, "hair_rig_target", expand=True)
|
|
row.scale_y = 2
|
|
|
|
column.separator()
|
|
|
|
grid = column.grid_flow(columns=1)
|
|
grid.prop(props, "hair_rig_bone_length", text="Bone Length (cm)", slider=True)
|
|
grid.prop(props, "hair_rig_bind_skip_length", text="Skip Length (cm)", slider=True)
|
|
grid.prop(props, "hair_rig_bind_trunc_length", text="Truncate Length (cm)", slider=True)
|
|
grid.prop(props, "hair_rig_bone_smoothing", text="Smoothing Steps", slider=True)
|
|
|
|
column.separator()
|
|
|
|
split = column.split(factor=0.45)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Group Name")
|
|
col_2.prop(props, "hair_rig_group_name", text="")
|
|
tool_row = col_1.row(align=True)
|
|
if arm:
|
|
depress = bones.is_bone_collection_visible(arm, "Spring (Edit)", vars.SPRING_EDIT_LAYER)
|
|
tool_row.operator("ccic.rigutils", icon=utils.check_icon("HIDE_OFF"), text="",
|
|
depress=depress).param = "TOGGLE_SHOW_SPRING_BONES"
|
|
is_grease_pencil_tool = "builtin.annotate" in utils.get_current_tool_idname(context)
|
|
tool_row.operator("cc3.hair", icon=utils.check_icon("GREASEPENCIL"), text="", depress=is_grease_pencil_tool).param = "TOGGLE_GREASE_PENCIL"
|
|
is_default_bone = False
|
|
if arm.data.display_type == 'WIRE':
|
|
icon = "IPO_LINEAR"
|
|
elif arm.data.display_type == 'OCTAHEDRAL' and arm.display_type == 'SOLID':
|
|
icon = "PMARKER_ACT"
|
|
is_default_bone = True
|
|
elif arm.data.display_type == 'OCTAHEDRAL' and arm.display_type == 'WIRE':
|
|
icon = "PMARKER_SEL"
|
|
elif arm.data.display_type == 'STICK':
|
|
icon = "FIXED_SIZE"
|
|
else:
|
|
icon = "BONE_DATA"
|
|
tool_row.operator("cc3.hair", icon=utils.check_icon(icon), text="", depress=False).param = "CYCLE_BONE_STYLE"
|
|
is_pose_position = rigutils.is_rig_rest_position(arm)
|
|
tool_row.operator("ccic.rigutils", icon="OUTLINER_OB_ARMATURE", text="", depress=is_pose_position).param = "TOGGLE_SHOW_RIG_POSE"
|
|
|
|
row = col_2.row()
|
|
row.operator("cc3.hair", icon=utils.check_icon("GROUP_BONE"), text="Rename").param = "GROUP_NAME_BONES"
|
|
row.enabled = edit_enabled
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.hair", icon=utils.check_icon("MOD_LATTICE"), text="Bones from Cards").param = "ADD_BONES"
|
|
row.enabled = edit_enabled
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.hair", icon=utils.check_icon("GREASEPENCIL"), text="Bones from Grease Pencil").param = "ADD_BONES_GREASE"
|
|
row.enabled = edit_enabled
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.hair", icon=utils.check_icon("GROUP_BONE"), text="Add Custom Bone").param = "ADD_BONES_CUSTOM"
|
|
row.enabled = edit_enabled
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.scale_y = 1
|
|
warn_icon(row, "X")
|
|
op_text = "Clear All Hair Bones" if props.hair_rig_bind_bone_mode == "ALL" else "Clear Selected Bones"
|
|
row.operator("cc3.hair", icon=utils.check_icon("BONE_DATA"), text=op_text).param = "REMOVE_HAIR_BONES"
|
|
row.enabled = edit_enabled
|
|
|
|
row = column.row()
|
|
row.scale_y = 1
|
|
warn_icon(row, "X")
|
|
op_text = "Clear All Hair Weights" if props.hair_rig_bind_bone_mode == "ALL" and props.hair_rig_bind_card_mode == "ALL" else "Clear Selected Weights"
|
|
row.operator("cc3.hair", icon=utils.check_icon("MOD_VERTEX_WEIGHT"), text=op_text).param = "CLEAR_WEIGHTS"
|
|
row.enabled = edit_enabled
|
|
|
|
row = column.row()
|
|
row.scale_y = 1
|
|
warn_icon(row, "X")
|
|
op_text = "Clear Grease Pencil"
|
|
row.operator("cc3.hair", icon=utils.check_icon("OUTLINER_OB_GREASEPENCIL"), text=op_text).param = "CLEAR_GREASE_PENCIL"
|
|
row.enabled = edit_enabled
|
|
|
|
column.separator()
|
|
|
|
grid = column.grid_flow(columns=1)
|
|
grid.row().prop(props, "hair_rig_bind_bone_mode", expand=True)
|
|
grid.row().prop(props, "hair_rig_bind_card_mode", expand=True)
|
|
grid.separator()
|
|
grid.prop(props, "hair_rig_bind_bone_radius", text="Bind Radius (cm)", slider=True)
|
|
grid.prop(props, "hair_rig_bind_bone_count", text="Bind Bones", slider=True)
|
|
grid.prop(props, "hair_rig_bind_bone_weight", text="Weight Scale", slider=True)
|
|
grid.prop(props, "hair_rig_bind_weight_curve", text="Weight Curve", slider=True)
|
|
grid.prop(props, "hair_rig_bind_bone_variance", text="Weight Variance", slider=True)
|
|
grid.prop(props, "hair_rig_bind_smoothing", text="Smoothing", slider=True)
|
|
grid.prop(props, "hair_rig_bind_seed", text="Random Seed", slider=True)
|
|
if props.hair_rig_target != "CC4":
|
|
grid.separator()
|
|
grid.prop(props, "hair_rig_bind_existing_scale", text="Scale Body Weights", slider=True)
|
|
column.separator()
|
|
if props.hair_rig_target == "CC4":
|
|
column.operator("cc3.hair", icon=utils.check_icon("X"), text="Reset Weights").param = "RESET_ACCESSORY_WEIGHTS"
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
op_text = "Bind Hair" if props.hair_rig_bind_card_mode == "ALL" and props.hair_rig_bind_bone_mode == "ALL" else "Bind Selected Hair"
|
|
row.operator("cc3.hair", icon=utils.check_icon("MOD_VERTEX_WEIGHT"), text=op_text).param = "BIND_TO_BONES"
|
|
row.enabled = edit_enabled
|
|
|
|
if chr_cache and arm and obj:
|
|
rigified_spring_rig = False
|
|
if chr_cache.rigified:
|
|
rigified_spring_rig = springbones.is_rigified(chr_cache, arm, chr_cache.available_spring_rigs)
|
|
if rigified_spring_rig is not None:
|
|
column.row().label(text="Rigify:", icon="OUTLINER_OB_ARMATURE")
|
|
row = column.row()
|
|
row.scale_y = 2
|
|
if rigified_spring_rig == True:
|
|
warn_icon(row)
|
|
row.operator("cc3.rigifier", icon="X", text="Remove Control Rig").param = "REMOVE_SPRING_RIG"
|
|
else:
|
|
row.operator("cc3.rigifier", icon="MOD_SCREW", text="Build Control Rig").param = "BUILD_SPRING_RIG"
|
|
column.separator()
|
|
|
|
if chr_cache and props.hair_rig_target == "CC4" and edit_enabled:
|
|
accessory_root = characters.get_accessory_root(chr_cache, obj)
|
|
spring_root = springbones.get_spring_rig(chr_cache, arm, props.hair_rig_bone_root)
|
|
if spring_root and accessory_root and accessory_root.name == spring_root.name:
|
|
#box.row().label(text = "For CC4 Accessory Only", icon="INFO")
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.hair", icon=utils.check_icon("FORWARD"), text="Finalize Accessory").param = "MAKE_ACCESSORY"
|
|
|
|
if chr_cache and arm and obj:
|
|
build_allowed = True
|
|
if chr_cache.rigified and not rigified_spring_rig:
|
|
build_allowed = False
|
|
rigid_body_sim_ui(chr_cache, arm, obj, layout, enabled=build_allowed)
|
|
|
|
|
|
class CC3HairPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Hair_Panel"
|
|
bl_label = "Curve Hair (WIP)"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
# Blender Curve Hair
|
|
|
|
if fake_drop_down(layout.box().row(),
|
|
"Blender Curve Hair",
|
|
"section_hair_blender_curve",
|
|
props.section_hair_blender_curve):
|
|
|
|
column = layout.column()
|
|
column.box().label(text="Exporting", icon="EXPORT")
|
|
column.row().operator("cc3.export_hair", icon=utils.check_icon("HAIR"), text="Export Hair")
|
|
column.row().prop(props, "hair_export_group_by", expand=True)
|
|
|
|
if not bpy.context.selected_objects:
|
|
column.enabled = False
|
|
|
|
column.separator()
|
|
|
|
# Hair curve extraction
|
|
column = layout.column()
|
|
column.box().label(text="Extract Curves", icon="EXPORT")
|
|
column.row().prop(props, "hair_curve_merge_loops", text="")
|
|
column.row().operator("cc3.hair", icon=utils.check_icon("HAIR"), text="Test").param = "CARDS_TO_CURVES"
|
|
|
|
if not bpy.context.selected_objects:
|
|
column.enabled = False
|
|
|
|
|
|
class CC3MaterialParametersPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Parameters_Panel"
|
|
bl_label = "Material Parameters"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
shader = "NONE"
|
|
parameters = None
|
|
if mat_cache:
|
|
parameters = mat_cache.parameters
|
|
|
|
render_rebuild_ui(layout, chr_cache)
|
|
|
|
render_prefs_ui(layout, 2)
|
|
|
|
# Parameters
|
|
|
|
if chr_cache and mat_cache and fake_drop_down(layout.box().row(),
|
|
"Adjust Parameters",
|
|
"stage4",
|
|
props.stage4):
|
|
|
|
# material selector
|
|
#column = layout.column()
|
|
#obj_cache = mat_cache = None
|
|
#mat_type = "NONE"
|
|
#if obj is not None:
|
|
# column.template_list("MATERIAL_UL_weightedmatslots", "", obj, "material_slots", obj, "active_material_index", rows=1)
|
|
|
|
column = layout.column()
|
|
column.enabled = not (utils.obj_is_linked(obj))
|
|
row = column.row()
|
|
row.prop(props, "update_mode", expand=True)
|
|
|
|
linked = props.update_mode == "UPDATE_LINKED"
|
|
has_key = chr_cache.get_import_has_key()
|
|
|
|
if chr_cache.setup_mode == "ADVANCED":
|
|
|
|
shader = params.get_shader_name(mat_cache)
|
|
bsdf_node, shader_node, mix_node = nodeutils.get_shader_nodes(mat, shader)
|
|
matrix = params.get_shader_def(shader)
|
|
|
|
if matrix and "ui" in matrix.keys():
|
|
|
|
ui_matrix = matrix["ui"]
|
|
|
|
column.separator()
|
|
|
|
box = column.box()
|
|
box.row().label(text = matrix["label"] + " Parameters", icon="MOD_HUE_SATURATION")
|
|
box.row().label(text = f"Material: {mat.name}", icon="SHADING_TEXTURE")
|
|
|
|
split = None
|
|
col_1 = None
|
|
col_2 = None
|
|
|
|
for ui_row in ui_matrix:
|
|
if ui_row[0] == "HEADER":
|
|
show_header = True
|
|
conditions = ui_row[3:]
|
|
for condition in conditions:
|
|
if condition == "HAS_VERTEX_COLORS":
|
|
cond_res = len(obj.data.vertex_colors) > 0
|
|
elif condition[0] == '#':
|
|
cond_res = chr_cache.get_render_target() == condition[1:]
|
|
elif condition[0] == '>':
|
|
cond_res = nodeutils.has_connected_output(shader_node, condition[1:])
|
|
elif condition[0] == '!':
|
|
condition = condition[1:]
|
|
cond_res = not nodeutils.has_connected_input(shader_node, condition)
|
|
else:
|
|
cond_res = nodeutils.has_connected_input(shader_node, condition)
|
|
if not cond_res:
|
|
show_header = False
|
|
if show_header:
|
|
column.box().label(text= ui_row[1], icon=utils.check_icon(ui_row[2]))
|
|
split = None
|
|
|
|
elif ui_row[0] == "WRINKLE_CONTROLS":
|
|
body_object = get_head_body_object_quick(chr_cache)
|
|
if body_object and "wrinkle_strength" in body_object:
|
|
column.box().label(text=ui_row[1], icon=utils.check_icon(ui_row[2]))
|
|
#row.label(text="", icon_value=iconutils.ICON_WRINKLE_REGIONS.icon_id)
|
|
if "wrinkle_source" in body_object:
|
|
row = column.row()
|
|
row.template_icon(icon_value=iconutils.ICON_WRINKLE_REGIONS.icon_id, scale=8)
|
|
if "wrinkle_source" in body_object:
|
|
row = column.row()
|
|
row.column().label(text="Region")
|
|
row.column().prop(props, "wrinkle_regions", text="")
|
|
else:
|
|
row = column.row()
|
|
row.alert = True
|
|
row.operator("cc3.importer", icon="DRIVER", text="Rebuild Drivers").param ="BUILD_DRIVERS"
|
|
row = column.row()
|
|
col_1 = row.column()
|
|
col_2 = row.column()
|
|
region = props.wrinkle_regions
|
|
if "wrinkle_source" in body_object:
|
|
if region == "ALL":
|
|
col_1.label(text="Strength")
|
|
col_2.prop(props, "wrinkle_strength", text="", slider=True)
|
|
else:
|
|
prop_name = "wrinkle_regions"
|
|
if prop_name in body_object:
|
|
col_1.label(text="Strength")
|
|
col_2.prop(body_object, f"[\"{prop_name}\"]", text="", slider=True, index=int(region)-1)
|
|
if "wrinkle_source" in body_object:
|
|
if region == "ALL":
|
|
col_1.label(text="Curve")
|
|
col_2.prop(props, "wrinkle_curve", text="", slider=True)
|
|
else:
|
|
prop_name = "wrinkle_curves"
|
|
if prop_name in body_object:
|
|
col_1.label(text="Curve")
|
|
col_2.prop(body_object, f"[\"{prop_name}\"]", text="", slider=True, index=int(region)-1)
|
|
column.separator()
|
|
if "wrinkle_strength" in body_object:
|
|
row = column.row()
|
|
row.column().label(text="Overall")
|
|
row.column().prop(body_object, "[\"wrinkle_strength\"]", text="", slider=True)
|
|
if "wrinkle_curve" in body_object:
|
|
row = column.row()
|
|
row.column().label(text="Curve")
|
|
row.column().prop(body_object, "[\"wrinkle_curve\"]", text="", slider=True)
|
|
|
|
elif ui_row[0] == "PROP":
|
|
|
|
show_prop = True
|
|
label = ui_row[1]
|
|
prop = ui_row[2]
|
|
is_slider = ui_row[3]
|
|
conditions = ui_row[4:]
|
|
alert = False
|
|
if len(label) > 0 and label.startswith("*"):
|
|
if has_key:
|
|
alert = True
|
|
label = label[1:]
|
|
|
|
if shader:
|
|
for condition in conditions:
|
|
if condition == "HAS_VERTEX_COLORS":
|
|
cond_res = len(obj.data.vertex_colors) > 0
|
|
elif condition[0] == '#':
|
|
cond_res = chr_cache.get_render_target() == condition[1:]
|
|
elif condition[0] == '>':
|
|
cond_res = nodeutils.has_connected_output(shader_node, condition[1:])
|
|
elif condition[0] == '!':
|
|
condition = condition[1:]
|
|
cond_res = not nodeutils.has_connected_input(shader_node, condition)
|
|
else:
|
|
cond_res = nodeutils.has_connected_input(shader_node, condition)
|
|
|
|
if not cond_res:
|
|
show_prop = False
|
|
|
|
if show_prop:
|
|
if not split:
|
|
split = column.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.alert = alert
|
|
col_1.label(text=label)
|
|
row = col_2.row(align=True)
|
|
row.prop(parameters, prop, text="", slider=is_slider)
|
|
|
|
elif ui_row[0] == "PAIR":
|
|
|
|
show_prop = True
|
|
label = ui_row[1]
|
|
prop_left = ui_row[2]
|
|
prop_right = ui_row[3]
|
|
is_slider = ui_row[4]
|
|
conditions = ui_row[5:]
|
|
alert = False
|
|
if len(label) > 0 and label.startswith("*"):
|
|
if has_key:
|
|
alert = True
|
|
label = label[1:]
|
|
|
|
if shader:
|
|
for condition in conditions:
|
|
if condition == "HAS_VERTEX_COLORS":
|
|
cond_res = len(obj.data.vertex_colors) > 0
|
|
elif condition[0] == '#':
|
|
cond_res = chr_cache.get_render_target() == condition[1:]
|
|
elif condition[0] == '>':
|
|
cond_res = nodeutils.has_connected_output(shader_node, condition[1:])
|
|
elif condition[0] == '!':
|
|
condition = condition[1:]
|
|
cond_res = not nodeutils.has_connected_input(shader_node, condition)
|
|
else:
|
|
cond_res = nodeutils.has_connected_input(shader_node, condition)
|
|
|
|
if not cond_res:
|
|
show_prop = False
|
|
|
|
if show_prop:
|
|
if not split:
|
|
split = column.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.alert = alert
|
|
col_1.label(text=label)
|
|
row = col_2.row(align=True)
|
|
row.prop(parameters, prop_left, text="", slider=is_slider)
|
|
row.prop(parameters, prop_right, text="", slider=is_slider)
|
|
|
|
elif ui_row[0] == "TRIPLET":
|
|
|
|
show_prop = True
|
|
label = ui_row[1]
|
|
prop_left = ui_row[2]
|
|
prop_mid = ui_row[3]
|
|
prop_right = ui_row[4]
|
|
is_slider = ui_row[5]
|
|
conditions = ui_row[6:]
|
|
alert = False
|
|
if len(label) > 0 and label.startswith("*"):
|
|
if has_key:
|
|
alert = True
|
|
label = label[1:]
|
|
|
|
if shader:
|
|
for condition in conditions:
|
|
if condition == "HAS_VERTEX_COLORS":
|
|
cond_res = len(obj.data.vertex_colors) > 0
|
|
elif condition[0] == '#':
|
|
cond_res = chr_cache.get_render_target() == condition[1:]
|
|
elif condition[0] == '>':
|
|
cond_res = nodeutils.has_connected_output(shader_node, condition[1:])
|
|
elif condition[0] == '!':
|
|
condition = condition[1:]
|
|
cond_res = not nodeutils.has_connected_input(shader_node, condition)
|
|
else:
|
|
cond_res = nodeutils.has_connected_input(shader_node, condition)
|
|
|
|
if not cond_res:
|
|
show_prop = False
|
|
|
|
if show_prop:
|
|
if not split:
|
|
split = column.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.alert = alert
|
|
col_1.label(text=label)
|
|
row = col_2.row(align=True)
|
|
row.prop(parameters, prop_left, text="", slider=is_slider)
|
|
row.prop(parameters, prop_mid, text="", slider=is_slider)
|
|
row.prop(parameters, prop_right, text="", slider=is_slider)
|
|
|
|
elif ui_row[0] == "OP":
|
|
|
|
show_op = True
|
|
label = ui_row[1]
|
|
op_id = ui_row[2]
|
|
icon = utils.check_icon(ui_row[3])
|
|
param = ui_row[4]
|
|
conditions = ui_row[5:]
|
|
|
|
if shader:
|
|
for condition in conditions:
|
|
if condition[0] == '!':
|
|
condition = condition[1:]
|
|
cond_res = not nodeutils.has_connected_input(shader_node, condition)
|
|
else:
|
|
cond_res = nodeutils.has_connected_input(shader_node, condition)
|
|
|
|
if not cond_res:
|
|
show_op = False
|
|
|
|
if show_op:
|
|
row = column.row()
|
|
row.operator(op_id, icon=icon, text=label).param = param
|
|
split = None
|
|
|
|
elif ui_row[0] == "SPACER":
|
|
if not split:
|
|
split = column.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.separator()
|
|
col_2.separator()
|
|
|
|
else:
|
|
|
|
basic_params = chr_cache.basic_parameters
|
|
bsdf_node = nodeutils.get_shader_nodes(mat, shader)[0]
|
|
|
|
column.separator()
|
|
column.box().label(text = "Basic Parameters:", icon="MOD_HUE_SATURATION")
|
|
|
|
actor_core = False
|
|
if chr_cache.generation == "ActorCore":
|
|
actor_core = True
|
|
|
|
if not actor_core:
|
|
column.box().label(text= "Skin", icon="OUTLINER_OB_ARMATURE")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Skin AO")
|
|
col_2.prop(basic_params, "skin_ao", text="", slider=True)
|
|
col_1.label(text="Skin Specular")
|
|
col_2.prop(basic_params, "skin_specular", text="", slider=True)
|
|
col_1.label(text="Skin Roughness")
|
|
col_2.prop(basic_params, "skin_roughness", text="", slider=True)
|
|
|
|
column.box().label(text= "Eyes", icon="MATSPHERE")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Eye Brightness")
|
|
col_2.prop(basic_params, "eye_brightness", text="", slider=True)
|
|
col_1.label(text="Eye Specular")
|
|
col_2.prop(basic_params, "eye_specular", text="", slider=True)
|
|
col_1.label(text="Eye Roughness")
|
|
col_2.prop(basic_params, "eye_roughness", text="", slider=True)
|
|
col_1.label(text="Eye Normal")
|
|
col_2.prop(basic_params, "eye_normal", text="", slider=True)
|
|
|
|
column.box().label(text= "Eye Occlusion", icon="PROP_CON")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Eye Occlusion")
|
|
col_2.prop(basic_params, "eye_occlusion", text="", slider=True)
|
|
col_1.label(text="Eye Occlusion Hardness")
|
|
col_2.prop(basic_params, "eye_occlusion_power", text="", slider=True)
|
|
|
|
column.box().label(text= "Tearline", icon="MATFLUID")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Tearline Alpha")
|
|
col_2.prop(basic_params, "tearline_alpha", text="", slider=True)
|
|
col_1.label(text="Tearline Roughness")
|
|
col_2.prop(basic_params, "tearline_roughness", text="", slider=True)
|
|
|
|
column.box().label(text= "Teeth", icon="RIGID_BODY")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Teeth Specular")
|
|
col_2.prop(basic_params, "teeth_specular", text="", slider=True)
|
|
col_1.label(text="Teeth Roughness")
|
|
col_2.prop(basic_params, "teeth_roughness", text="", slider=True)
|
|
|
|
column.box().label(text= "Tongue", icon="INVERSESQUARECURVE")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Tongue Specular")
|
|
col_2.prop(basic_params, "tongue_specular", text="", slider=True)
|
|
col_1.label(text="Tongue Roughness")
|
|
col_2.prop(basic_params, "tongue_roughness", text="", slider=True)
|
|
|
|
column.box().label(text= "Hair", icon=utils.check_icon("OUTLINER_OB_HAIR"))
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Hair AO")
|
|
col_2.prop(basic_params, "hair_ao", text="", slider=True)
|
|
col_1.label(text="Hair Specular")
|
|
col_2.prop(basic_params, "hair_specular", text="", slider=True)
|
|
col_1.label(text="Scalp Specular")
|
|
col_2.prop(basic_params, "scalp_specular", text="", slider=True)
|
|
col_1.label(text="Hair Bump Height (mm)")
|
|
col_2.prop(basic_params, "hair_bump", text="", slider=True)
|
|
|
|
if actor_core:
|
|
column.box().label(text= "Actor Core", icon="USER")
|
|
else:
|
|
column.box().label(text= "Default", icon="CUBE")
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Default AO")
|
|
col_2.prop(basic_params, "default_ao", text="", slider=True)
|
|
col_1.label(text="Default Specular")
|
|
col_2.prop(basic_params, "default_specular", text="", slider=True)
|
|
if not actor_core:
|
|
col_1.label(text="Default Bump Height (mm)")
|
|
col_2.prop(basic_params, "default_bump", text="", slider=True)
|
|
|
|
# Channel Mixers
|
|
|
|
if chr_cache and mat_cache and chr_cache.setup_mode == "ADVANCED":
|
|
|
|
mixer_settings = mat_cache.mixer_settings
|
|
|
|
if chr_cache and mat_cache and fake_drop_down(layout.box().row(),
|
|
"Texture Channel Mixer",
|
|
"stage_remapper",
|
|
props.stage_remapper):
|
|
|
|
column = layout.column()
|
|
column.enabled = not utils.obj_is_linked(obj)
|
|
|
|
show_channels = False
|
|
|
|
for mixer_ref in channel_mixer.MIXER_CHANNELS:
|
|
|
|
if type(mixer_ref) == str:
|
|
|
|
if mixer_ref == "RGB_HEADER":
|
|
column.box().label(text="RGB Mask", icon="RESTRICT_COLOR_ON")
|
|
column.label(text = "RGB Mask Image:")
|
|
if mixer_settings.rgb_image:
|
|
column.template_ID_preview(mixer_settings, "rgb_image", open="image.open")
|
|
show_channels = True
|
|
else:
|
|
column.template_ID(mixer_settings, "rgb_image", open="image.open", live_icon=True)
|
|
show_channels = False
|
|
|
|
elif mixer_ref == "ID_HEADER":
|
|
column.separator()
|
|
column.box().label(text="Color ID Mask", icon="GROUP_VCOL")
|
|
column.label(text = "Color ID Mask Image:")
|
|
if mixer_settings.id_image:
|
|
column.template_ID_preview(mixer_settings, "id_image", open="image.open")
|
|
show_channels = True
|
|
else:
|
|
column.template_ID(mixer_settings, "id_image", open="image.open", live_icon=True)
|
|
show_channels = False
|
|
|
|
elif show_channels:
|
|
|
|
mixer_label = mixer_ref[0]
|
|
mixer_on_prop = mixer_ref[1]
|
|
mixer_type_channel = mixer_ref[2]
|
|
mixer_type, mixer_channel = mixer_type_channel.split("_")
|
|
mixer = mixer_settings.get_mixer(mixer_type, mixer_channel)
|
|
|
|
column = layout.column()
|
|
box = column.box()
|
|
split = box.split(factor=0.75)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
|
|
expanded = False
|
|
if mixer:
|
|
expanded = mixer.expanded
|
|
|
|
row = col_1.row()
|
|
if expanded:
|
|
row.prop(mixer, "expanded", icon="TRIA_DOWN", icon_only=True, emboss=False)
|
|
elif mixer:
|
|
row.prop(mixer, "expanded", icon="TRIA_RIGHT", icon_only=True, emboss=False)
|
|
row.label(text=mixer_label)
|
|
row = col_2.row()
|
|
row.prop(mixer_settings, mixer_on_prop, text="", slider=True)
|
|
if mixer:
|
|
op = row.operator("cc3.mixer", icon="PANEL_CLOSE", text = "", emboss = False)
|
|
op.param = "REMOVE"
|
|
op.type_channel = mixer_type_channel
|
|
|
|
if mixer and mixer.enabled and expanded:
|
|
|
|
main_column = layout.column()
|
|
split = main_column.split(factor=0.01)
|
|
gutter = split.column()
|
|
column = split.column()
|
|
|
|
for ui_row in channel_mixer.MIXER_UI:
|
|
|
|
split = False
|
|
col_1 = None
|
|
col_2 = None
|
|
|
|
if ui_row[0] == "HEADER":
|
|
column.box().label(text= ui_row[1], icon=ui_row[2])
|
|
|
|
elif ui_row[0] == "PROP":
|
|
|
|
label = ui_row[1]
|
|
prop = ui_row[2]
|
|
|
|
if not split:
|
|
row = column.row()
|
|
split = row.split(factor=0.5)
|
|
col_1 = row.column()
|
|
col_2 = row.column()
|
|
split = True
|
|
col_1.label(text=label)
|
|
col_2.prop(mixer, prop, text="", slider=True)
|
|
|
|
# Utilities
|
|
|
|
layout.box().label(text="Utilities", icon="MODIFIER_DATA")
|
|
column = layout.column()
|
|
if not chr_cache:
|
|
column.enabled = False
|
|
if chr_cache and chr_cache.is_import_type("FBX"):
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Open Mouth")
|
|
if chr_cache:
|
|
col_2.prop(chr_cache, "open_mouth", text="", slider=True)
|
|
else:
|
|
col_2.prop(props, "dummy_slider", text="", slider=True)
|
|
|
|
col_1.label(text="Eye Close")
|
|
if chr_cache:
|
|
col_2.prop(chr_cache, "eye_close", text="", slider=True)
|
|
else:
|
|
col_2.prop(props, "dummy_slider", text="", slider=True)
|
|
|
|
column = layout.column()
|
|
if not chr_cache:
|
|
column.enabled = False
|
|
op = column.operator("cc3.setproperties", icon="DECORATE_OVERRIDE", text="Reset Parameters")
|
|
op.param = "RESET"
|
|
|
|
|
|
class CC3RigifyPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Rigify_Panel"
|
|
bl_label = "Rigging & Animation"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
missing_materials = characters.has_missing_materials(chr_cache)
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
width = get_layout_width(context, "UI")
|
|
|
|
rigify_installed = rigging.is_rigify_installed()
|
|
|
|
if rigify_installed:
|
|
|
|
if chr_cache:
|
|
|
|
rig = chr_cache.get_armature()
|
|
is_face_rig = rigutils.is_face_rig(rig)
|
|
face_profile, viseme_profile = chr_cache.get_facial_profile_names(update=False)
|
|
|
|
box = layout.box()
|
|
split = box.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text = "Character:")
|
|
col_2.label(text = chr_cache.character_name)
|
|
col_1.label(text = "Generation:")
|
|
col_2.label(text = chr_cache.generation)
|
|
col_1.label(text = "Face Profile:")
|
|
col_2.label(text = face_profile)
|
|
col_1.label(text = "Viseme:")
|
|
col_2.label(text = viseme_profile)
|
|
if chr_cache.rigified:
|
|
col_1.label(text = "Rig Type:")
|
|
col_2.label(text = "Rigify")
|
|
col_1.label(text = "Face Rig:")
|
|
col_2.label(text = utils.get_enum_prop_name(chr_cache, "rigify_expression_rig"))
|
|
|
|
if chr_cache.generation == "ActorCore":
|
|
box.row().operator("cc3.character", icon="MATERIAL", text="Match Existing Materials").param = "MATCH_MATERIALS"
|
|
if missing_materials:
|
|
box.row().operator("cc3.character", icon="ADD", text="Convert Materials").param = "ADD_MATERIALS"
|
|
|
|
layout.separator()
|
|
|
|
if fake_drop_down(layout.box().row(),
|
|
"Rigify Setup",
|
|
"section_rigify_setup",
|
|
props.section_rigify_setup,
|
|
icon="OUTLINER_OB_ARMATURE", icon_closed="OUTLINER_OB_ARMATURE"):
|
|
|
|
row = layout.row()
|
|
row.prop(chr_cache, "rig_mode", expand=True)
|
|
|
|
if chr_cache.rigified:
|
|
|
|
if obj == chr_cache.rig_meta_rig:
|
|
|
|
layout.row().label(text="Re-rigify", icon="INFO")
|
|
col = layout.column(align=True)
|
|
col.label(text="Expression Rig:")
|
|
col.row(align=True).prop(prefs, "rigify_expression_rig", expand=True)
|
|
col = layout.column()
|
|
if prefs.rigify_expression_rig == "META":
|
|
col.row().prop(prefs, "rigify_face_control_color")
|
|
row = col.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.rigifier", icon="OUTLINER_OB_ARMATURE", text="Regenerate Rigify").param = "RE_RIGIFY_META"
|
|
|
|
layout.separator()
|
|
|
|
if chr_cache.rigified_full_face_rig:
|
|
|
|
layout.row().label(text = "Face Rig Re-Parenting", icon = "INFO")
|
|
|
|
if chr_cache.rig_mode == "ADVANCED":
|
|
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier", icon="LOCKED", text="Lock Non-Face VGroups").param = "LOCK_NON_FACE_VGROUPS"
|
|
row.enabled = chr_cache is not None
|
|
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier", icon="MESH_DATA", text="Clean Body Mesh").param = "CLEAN_BODY_MESH"
|
|
row.enabled = chr_cache is not None
|
|
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier", icon="ANIM_DATA", text="Reparent Auto Weights").param = "REPARENT_RIG"
|
|
row.enabled = chr_cache is not None
|
|
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier", icon="UNLOCKED", text="Unlock VGroups").param = "UNLOCK_VGROUPS"
|
|
row.enabled = chr_cache is not None
|
|
|
|
else:
|
|
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier", icon="ANIM_DATA", text="With Automatic Weights").param = "REPARENT_RIG_SEPARATE_HEAD_QUICK"
|
|
row.enabled = chr_cache is not None
|
|
|
|
if rigging.is_surface_heat_voxel_skinning_installed():
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier_modal", icon="COMMUNITY", text="Voxel Skinning").param = "VOXEL_SURFACE_REPARENT"
|
|
row.enabled = chr_cache is not None
|
|
|
|
layout.separator()
|
|
|
|
else:
|
|
|
|
layout.row().label(text = "Rigging Done.", icon = "INFO")
|
|
layout.separator()
|
|
|
|
elif chr_cache.can_be_rigged():
|
|
|
|
allow_rigify = chr_cache.allow_rigify()
|
|
grid = layout.grid_flow(columns=1, row_major=True, align=True)
|
|
grid.prop(prefs, "rigify_auto_retarget", text = "Auto retarget", toggle=True)
|
|
|
|
if prefs.rigify_auto_retarget:
|
|
# retarget/bake motion prefix
|
|
row = layout.row()
|
|
split = row.split(factor=0.45, align=True)
|
|
split.column().label(text="Motion Prefix")
|
|
row = split.column().row(align=True)
|
|
row.prop(props, "rigify_retarget_motion_prefix", text="")
|
|
icon = "FAKE_USER_OFF" if not props.rigify_retarget_use_fake_user else "FAKE_USER_ON"
|
|
row.prop(props, "rigify_retarget_use_fake_user", text="", icon=icon, toggle=True)
|
|
|
|
if chr_cache.can_expression_rig() or chr_cache.can_rigify_face():
|
|
col = layout.column(align=True)
|
|
col.label(text="Expression Rig:")
|
|
col.row(align=True).prop(prefs, "rigify_expression_rig", expand=True)
|
|
col = layout.column()
|
|
if prefs.rigify_expression_rig == "META":
|
|
if allow_rigify:
|
|
col.row().prop(prefs, "rigify_face_control_color")
|
|
else:
|
|
wrapped_text_box(layout, "Invalid Facial Profile!", width, alert=True)
|
|
elif prefs.rigify_expression_rig == "RIGIFY":
|
|
if not chr_cache.can_rigify_face():
|
|
wrapped_text_box(layout, "Note: Full face rig cannot be auto-detected for this character.", width)
|
|
else:
|
|
grid = layout.grid_flow(columns=1, row_major=True, align=True)
|
|
|
|
col = layout.column(align=True)
|
|
col.label(text="Bone Alignment:")
|
|
col.row(align=True).prop(prefs, "rigify_align_bones", expand=True)
|
|
|
|
if chr_cache.rig_mode == "QUICK":
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.rigifier", icon="OUTLINER_OB_ARMATURE", text="Rigify").param = "ALL"
|
|
row.enabled = allow_rigify
|
|
|
|
else:
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.rigifier", icon="MOD_ARMATURE", text="Attach Meta-Rig").param = "META_RIG"
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.rigifier", icon="OUTLINER_OB_ARMATURE", text="Generate Rigify").param = "RIGIFY_META"
|
|
row.enabled = allow_rigify
|
|
|
|
#row = layout.row()
|
|
#row.scale_y = 2
|
|
#row.operator("cc3.rigifier", icon="MOD_ARMATURE", text="REPORT FACE TARGETS").param = "REPORT_FACE_TARGETS"
|
|
#row.enabled = chr_cache is not None
|
|
|
|
else:
|
|
wrapped_text_box(layout, "This character cannot be rigged.", width)
|
|
|
|
if chr_cache.rigified:
|
|
|
|
has_spring_rigs = springbones.has_spring_rigs(chr_cache, rig)
|
|
ik_fk = rigutils.get_rigify_ik_fk_influence_avg(rig)
|
|
|
|
# utility widgets minipanel
|
|
box_row = layout.box().row(align=True)
|
|
is_full_rig_show = rigutils.is_full_rigify_rig_shown(rig)
|
|
box_row.operator("ccic.rigutils", icon="HIDE_OFF", text="", depress=is_full_rig_show).param = "TOGGLE_SHOW_FULL_RIG"
|
|
if has_spring_rigs:
|
|
is_base_rig_show = rigutils.is_base_rig_shown(rig)
|
|
box_row.operator("ccic.rigutils", icon="ARMATURE_DATA", text="", depress=is_base_rig_show).param = "TOGGLE_SHOW_BASE_RIG"
|
|
is_spring_rig_show = rigutils.is_spring_rig_shown(rig)
|
|
box_row.operator("ccic.rigutils", icon="FORCE_MAGNETIC", text="", depress=is_spring_rig_show).param = "TOGGLE_SHOW_SPRING_RIG"
|
|
is_pose_position = rigutils.is_rig_rest_position(rig)
|
|
box_row.operator("ccic.rigutils", icon="OUTLINER_OB_ARMATURE", text="", depress=is_pose_position).param = "TOGGLE_SHOW_RIG_POSE"
|
|
box_row.operator("ccic.rigutils", icon="LOOP_BACK", text="").param = "BUTTON_RESET_POSE_SELECTED"
|
|
#box_row.operator("ccic.rigutils", icon="X", text="").param = "BUTTON_RESET_POSE"
|
|
box_row.separator()
|
|
depress = True if ik_fk > 0.995 else False
|
|
box_row.operator("ccic.rigutils", text="FK", depress=depress).param = "SET_LIMB_FK"
|
|
depress = True if ik_fk < 0.005 else False
|
|
box_row.operator("ccic.rigutils", text="IK", depress=depress).param = "SET_LIMB_IK"
|
|
|
|
|
|
if has_spring_rigs:
|
|
|
|
if fake_drop_down(layout.box().row(),
|
|
"Spring Rigs",
|
|
"section_rigify_spring",
|
|
props.section_rigify_spring,
|
|
icon="FORCE_MAGNETIC", icon_closed="FORCE_MAGNETIC"):
|
|
|
|
split = layout.split(factor=0.45)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Spring Rig")
|
|
col_2.prop(chr_cache, "available_spring_rigs", text="")
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
rigified_control_rig = springbones.is_rigified(chr_cache, rig, chr_cache.available_spring_rigs)
|
|
if rigified_control_rig:
|
|
warn_icon(row)
|
|
row.operator("cc3.rigifier", icon="X", text="Remove Control Rig").param = "REMOVE_SPRING_RIG"
|
|
else:
|
|
row.operator("cc3.rigifier", icon="MOD_SCREW", text="Build Control Rig").param = "BUILD_SPRING_RIG"
|
|
|
|
layout.separator()
|
|
|
|
rigid_body_sim_ui(chr_cache, rig, obj, layout, show_selector=False, enabled=rigified_control_rig)
|
|
|
|
layout.separator()
|
|
|
|
box_row = layout.box().row()
|
|
if fake_drop_down(box_row,
|
|
"Rig Controls",
|
|
"section_rigify_controls",
|
|
props.section_rigify_controls,
|
|
icon="TOOL_SETTINGS", icon_closed="TOOL_SETTINGS"):
|
|
|
|
if False: # disabling IK stretch is a bad idea...
|
|
row = layout.row()
|
|
if rigutils.is_stretch_enabled(rig):
|
|
row.operator("ccic.rigutils", icon="X", text="Disable All IK Stretch").param = "DISABLE_CONSTRAINT_STRETCH"
|
|
else:
|
|
row.operator("ccic.rigutils", icon="CON_STRETCHTO", text="Re-enable IK Stretch").param = "ENABLE_CONSTRAINT_STRETCH"
|
|
|
|
num_splits = 0
|
|
for control_name, control_def in rigify_mapping_data.IKFK_RIG_CONTROLS.items():
|
|
if len(control_def) == 4 and type(control_def[0]) is str:
|
|
num_splits, split_fac = control_def[3]
|
|
split = layout.split(factor=split_fac)
|
|
col_1 = col_2 = col_3 = None
|
|
if num_splits >= 1:
|
|
col_1 = split.column()
|
|
col_1.label(text=control_def[0])
|
|
if num_splits >= 2:
|
|
col_2 = split.column()
|
|
col_2.label(text=control_def[1])
|
|
if num_splits >= 3:
|
|
col_3 = split.column()
|
|
col_3.label(text=control_def[2])
|
|
else:
|
|
prop_def_1 = prop_def_2 = prop_def_3 = None
|
|
if len(control_def) >= 1:
|
|
prop_def_1 = control_def[0]
|
|
if len(control_def) >= 2:
|
|
prop_def_2 = control_def[1]
|
|
if len(control_def) >= 3:
|
|
prop_def_3 = control_def[2]
|
|
if prop_def_1:
|
|
col_1.prop(rig.pose.bones[prop_def_1[0]], f"[\"{prop_def_1[1]}\"]", text=prop_def_1[2], slider=True)
|
|
elif col_1:
|
|
col_1.label(text="")
|
|
if prop_def_2:
|
|
col_2.prop(rig.pose.bones[prop_def_2[0]], f"[\"{prop_def_2[1]}\"]", text=prop_def_2[2], slider=True)
|
|
elif col_2:
|
|
col_2.label(text="")
|
|
if prop_def_3:
|
|
col_3.prop(rig.pose.bones[prop_def_3[0]], f"[\"{prop_def_3[1]}\"]", text=prop_def_3[2], slider=True)
|
|
elif col_3:
|
|
col_3.label(text="")
|
|
|
|
if is_face_rig:
|
|
split = layout.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
facerig_bone = rig.pose.bones["facerig"]
|
|
col_1.label(text="Face Rig:")
|
|
col_row = col_2.row(align=True)
|
|
is_facerig_shown, is_only_facerig_shown = rigutils.is_only_face_rig_shown(rig)
|
|
col_row.operator("ccic.rigutils", icon=("HIDE_OFF" if is_facerig_shown else "HIDE_ON"),
|
|
text="", depress=is_only_facerig_shown).param = "TOGGLE_SHOW_FACE_RIG"
|
|
col_row.operator("ccic.rigutils", icon="LOOP_BACK", text="").param = "RESET_EXPRESSION_POSE_SELECTED"
|
|
#col_row.operator("ccic.rigutils", icon="X", text="").param = "RESET_EXPRESSION_POSE"
|
|
col_row.prop(chr_cache, "rigify_face_control_color", text="")
|
|
col_1.label(text="Head Follow")
|
|
col_row = col_2.row(align=True)
|
|
col_row.prop(facerig_bone, "[\"head_follow\"]", slider=True, text="")
|
|
facerig_locked = facerig_bone.bone.hide_select
|
|
col_row.operator("ccic.rigutils", icon="LOCKED" if facerig_locked else "UNLOCKED", text="").param = "TOGGLE_EXPRESSION_RIG_LOCK"
|
|
col_1.label(text="Eyes Track")
|
|
col_row = col_2.row(align=True)
|
|
col_row.prop(facerig_bone, "[\"eyes_track\"]", slider=True, text="")
|
|
layout.label(text="Overall Strength")
|
|
row = layout.row(align=True)
|
|
row.prop(facerig_bone, "[\"key_strength\"]", slider=True, text="Key")
|
|
row.prop(facerig_bone, "[\"bone_strength\"]", slider=True, text="Bone")
|
|
|
|
|
|
box_row = layout.box().row()
|
|
if fake_drop_down(box_row,
|
|
"Retargeting",
|
|
"section_rigify_retarget",
|
|
props.section_rigify_retarget,
|
|
icon="ARMATURE_DATA", icon_closed="ARMATURE_DATA"):
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
#row.operator("cc3.importer", icon="OUTLINER_OB_ARMATURE", text="Imp. Character").param = "IMPORT"
|
|
row.operator("cc3.anim_importer", icon="ARMATURE_DATA", text="Import Animations")
|
|
|
|
layout.label(text="Source Armature:")
|
|
|
|
layout.template_list("ARMATURE_UL_List", "bake_armature_list", bpy.data, "objects", props, "armature_list_index", rows=1, maxrows=4)
|
|
|
|
split = layout.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Source Action:")
|
|
col_2.prop(props, "armature_action_filter", text="Filter by Rig")
|
|
|
|
layout.template_list("ACTION_UL_List", "bake_action_list", bpy.data, "actions", props, "action_list_index", rows=1, maxrows=5)
|
|
|
|
armature_list_object = utils.collection_at_index(props.armature_list_index, bpy.data.objects)
|
|
action_list_action = utils.collection_at_index(props.action_list_index, bpy.data.actions)
|
|
source_type = "Unknown"
|
|
if armature_list_object:
|
|
source_type, source_label = rigutils.get_armature_action_source_type(armature_list_object, action_list_action)
|
|
if source_type:
|
|
layout.box().label(text = f"{source_label} Animation", icon = "ARMATURE_DATA")
|
|
|
|
if True:
|
|
layout.label(text="Limb Correction:")
|
|
column = layout.column()
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Arms")
|
|
col_2.prop(chr_cache, "retarget_arm_correction_angle", text="", slider=True)
|
|
col_1.label(text="Legs")
|
|
col_2.prop(chr_cache, "retarget_leg_correction_angle", text="", slider=True)
|
|
col_1.label(text="Heels")
|
|
col_2.prop(chr_cache, "retarget_heel_correction_angle", text="", slider=True)
|
|
col_1.label(text="Height")
|
|
col_2.prop(chr_cache, "retarget_z_correction_height", text="", slider=True)
|
|
layout.separator()
|
|
|
|
# retarget and bake armature actions
|
|
col = layout.column(align=True)
|
|
|
|
row = col.row(align=True)
|
|
row.prop(prefs, "rigify_preview_retarget_fk_ik", expand=True)
|
|
|
|
row = col.row(align=True)
|
|
row.scale_y = 1.5
|
|
retarget_rig = chr_cache.rig_retarget_rig
|
|
if utils.object_exists_is_armature(retarget_rig):
|
|
depress = True
|
|
row.operator("cc3.rigifier", icon="X", text="Stop Preview", depress=depress).param = "RETARGET_CC_REMOVE_PAIR"
|
|
else:
|
|
depress = False
|
|
row.operator("cc3.rigifier", icon="HIDE_OFF", text="Preview Retarget", depress=depress).param = "RETARGET_CC_PAIR_RIGS"
|
|
row.enabled = source_type != "Unknown"
|
|
|
|
row = col.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.rigifier", icon="ANIM_DATA", text="Bake Retarget").param = "RETARGET_CC_BAKE_ACTION"
|
|
if source_type == "Unknown" and chr_cache.rig_retarget_rig is None:
|
|
row.enabled = False
|
|
|
|
col.separator()
|
|
|
|
# retarget/bake motion prefix
|
|
row = col.row()
|
|
split = row.split(factor=0.45)
|
|
split.column().label(text="Motion Prefix")
|
|
row = split.column().row(align=True)
|
|
row.prop(props, "rigify_retarget_motion_prefix", text="")
|
|
icon = "FAKE_USER_OFF" if not props.rigify_retarget_use_fake_user else "FAKE_USER_ON"
|
|
row.prop(props, "rigify_retarget_use_fake_user", text="", icon=icon, toggle=True)
|
|
|
|
if fake_drop_down(layout.box().row(),
|
|
"Export",
|
|
"section_rigify_export",
|
|
props.section_rigify_export,
|
|
icon="EXPORT", icon_closed="EXPORT"):
|
|
|
|
export_bake_action, export_bake_source_type = rigging.get_bake_action(chr_cache)
|
|
box = layout.box()
|
|
if export_bake_source_type == "RIGIFY":
|
|
box.label(text="Export from: Rigify Action")
|
|
elif export_bake_source_type == "RETARGET":
|
|
box.label(text="Export from: Retarget Action")
|
|
else:
|
|
box.label(text="Export from: NLA")
|
|
if export_bake_action:
|
|
box.row().label(text=export_bake_action.name)
|
|
|
|
rigify_export_group(chr_cache, layout)
|
|
|
|
if chr_cache:
|
|
|
|
box_row = layout.box().row()
|
|
if fake_drop_down(box_row,
|
|
"Motion Sets",
|
|
"section_rigify_action_sets",
|
|
props.section_rigify_action_sets,
|
|
icon="ANIM_DATA", icon_closed="ANIM_DATA"):
|
|
|
|
layout.label(text="Import Options:")
|
|
row = layout.row(align=True)
|
|
label = rigutils.CCICActionImportOptions.label(context)
|
|
row.operator("ccic.action_import_options", icon="COLLAPSEMENU", text=label)
|
|
column = layout.column()
|
|
motion_set_ui(column, chr_cache)
|
|
|
|
box_row = layout.box().row()
|
|
if fake_drop_down(box_row,
|
|
"ARKit",
|
|
"section_rigify_arkit",
|
|
props.section_rigify_arkit,
|
|
icon="ANIM_DATA", icon_closed="ANIM_DATA"):
|
|
has_proxy = utils.object_exists_is_armature(chr_cache.arkit_proxy)
|
|
row = layout.row()
|
|
row.scale_y = 2.0
|
|
if not has_proxy:
|
|
row.operator("cc3.rigifier", icon="MONKEY", text="Add ARKit Proxy").param = "ARKIT_PROXY_ADD"
|
|
else:
|
|
row.alert=True
|
|
row.operator("cc3.rigifier", icon="X", text="Remove ARKit Proxy").param = "ARKIT_PROXY_REMOVE"
|
|
if has_proxy:
|
|
proxy_rig = chr_cache.arkit_proxy
|
|
|
|
# load csv button
|
|
row = layout.row()
|
|
row.scale_y = 2.0
|
|
row.operator("ccic.import_arkit_csv", icon="KEYINGSET", text="Load CSV").param = ""
|
|
if proxy_rig["csv_file"]:
|
|
row = layout.row()
|
|
row.prop(proxy_rig, "[\"csv_file\"]", text="")
|
|
|
|
# load csv params
|
|
col = layout.column(align=True)
|
|
col.row().prop(proxy_rig, "[\"filter\"]", text="Smoothing", slider=True)
|
|
col.row().prop(proxy_rig, "[\"random_variance\"]", text="Variance", slider=True)
|
|
col.row().prop(proxy_rig, "[\"random_seed\"]", text="Seed")
|
|
|
|
# reload button
|
|
if proxy_rig["csv_file"]:
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
row.operator("ccic.import_arkit_csv", icon="KEYINGSET", text="Reload & Apply").param="RELOAD"
|
|
|
|
# shapekey driver adjust params
|
|
layout.row().label(text="Adjust:")
|
|
col = layout.column(align=True)
|
|
col.row().prop(proxy_rig, "[\"strength\"]", text="Strength", slider=True)
|
|
if proxy_rig["relaxation"] >= 1.25:
|
|
text = "Strong Relax"
|
|
elif proxy_rig["relaxation"] < 0.75:
|
|
text = "Strong Exaggerate"
|
|
elif proxy_rig["relaxation"] < 0.95:
|
|
text = "Exaggerate"
|
|
elif proxy_rig["relaxation"] >= 1.05:
|
|
text = "Relax"
|
|
else:
|
|
text = "Normal"
|
|
col.row().prop(proxy_rig, "[\"relaxation\"]", text=text, slider=True)
|
|
col.row().prop(proxy_rig, "[\"horizontal_bias\"]", text="L/R Bias", slider=True)
|
|
col.row().prop(proxy_rig, "[\"vertical_bias\"]", text="U/D Bias", slider=True)
|
|
|
|
# bone driver adjust params
|
|
layout.row().label(text="Bones:")
|
|
col = layout.column(align=True)
|
|
col.row().prop(proxy_rig, "[\"head_blend\"]", text="Head Blend", slider=True)
|
|
col.separator()
|
|
col.row().prop(proxy_rig, "[\"head_yaw_offset\"]", text="Yaw Adjust", slider=True)
|
|
col.row().prop(proxy_rig, "[\"head_pitch_offset\"]", text="Pitch Adjust", slider=True)
|
|
col.row().prop(proxy_rig, "[\"head_roll_offset\"]", text="Roll Adjust", slider=True)
|
|
|
|
layout.row().label(text="Bake:")
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.rigifier", icon="ANIM_DATA", text="Bake NLA").param = "NLA_ARKIT_BAKE"
|
|
split = layout.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Motion ID:")
|
|
col_2.prop(proxy_rig, "[\"bake_motion_id\"]", text="")
|
|
col_1.label(text="Motion Prefix:")
|
|
col_2.prop(proxy_rig, "[\"bake_motion_prefix\"]", text="")
|
|
|
|
elif not chr_cache:
|
|
|
|
reconnect_character_ui(context, layout, chr_cache)
|
|
|
|
else:
|
|
wrapped_text_box(layout, "Rigify add-on is not enabled.", width, True)
|
|
|
|
|
|
def motion_set_ui(layout: bpy.types.UILayout, chr_cache, show_nla=False):
|
|
props = vars.props()
|
|
|
|
# current selected motion set action
|
|
action_set_list_action = utils.collection_at_index(props.action_set_list_index, bpy.data.actions)
|
|
action_set_generation = rigutils.get_set_generation(action_set_list_action)
|
|
rig = None
|
|
rig_set_generation = None
|
|
if chr_cache:
|
|
rig = chr_cache.get_armature()
|
|
rig_set_generation = rigutils.get_set_generation(rig)
|
|
|
|
col = layout.column(align=True)
|
|
split = col.split(factor=0.4)
|
|
split.column().label(text="Motion Sets:")
|
|
split.column().row().prop(props, "filter_motion_set_type", text="Filter")
|
|
|
|
col.template_list("ACTION_SET_UL_List", "action_set_list", bpy.data, "actions", props, "action_set_list_index", rows=5, maxrows=5)
|
|
|
|
row = col.row(align=True)
|
|
row.operator("ccic.motion_set_rename", icon="GREASEPENCIL", text="Rename")
|
|
row.operator("ccic.motion_set_info", icon="VIEWZOOM", text="")
|
|
row.operator("ccic.motion_set_funcs", icon="DUPLICATE", text="").param = "DUPLICATE"
|
|
row.operator("ccic.motion_set_funcs", icon="TRASH", text="").param = "DELETE"
|
|
depress = False
|
|
if action_set_list_action:
|
|
depress = action_set_list_action.use_fake_user
|
|
icon = "FAKE_USER_ON" if depress else "FAKE_USER_OFF"
|
|
param = "SET_FAKE_USER_OFF" if depress else "SET_FAKE_USER_ON"
|
|
row.operator("ccic.rigutils", icon=icon, text="", depress=depress).param = param
|
|
|
|
|
|
split = col.split(factor=0.5, align=True)
|
|
split.scale_y = 1.5
|
|
col_1 = split.column(align=True)
|
|
col_2 = split.column(align=True)
|
|
row = col_1.row(align=True)
|
|
row.operator("ccic.rigutils", icon="ACTION_TWEAK", text="Load").param = "LOAD_MOTION_SET"
|
|
if rig_set_generation != action_set_generation:
|
|
row.enabled = False
|
|
col_2.operator("ccic.rigutils", icon="REMOVE", text="Clear").param = "CLEAR_ACTION_SET"
|
|
col_1.separator()
|
|
col_2.separator()
|
|
col_1.operator("ccic.rigutils", icon="PLUS", text="New").param = "NEW_ACTION_SET"
|
|
col_2.operator("ccic.rigutils", icon="MODIFIER_ON", text="Clean").param = "CLEAN_ACTIONS"
|
|
|
|
|
|
if show_nla:
|
|
row = col_1.row(align=True)
|
|
row.operator("ccic.rigutils", icon="NLA_PUSHDOWN", text="Push").param = "PUSH_ACTION_SET"
|
|
if rig_set_generation != action_set_generation:
|
|
row.enabled = False
|
|
col_2.operator("ccic.rigutils", icon="RESTRICT_SELECT_OFF", text="Select").param = "SELECT_SET_STRIPS"
|
|
|
|
# strip tools
|
|
active_strip = bpy.context.active_nla_strip
|
|
split = layout.split(align=True)
|
|
col_1 = split.column(align=True)
|
|
col_2 = split.column(align=True)
|
|
col_3 = split.column(align=True)
|
|
col_4 = split.column(align=True)
|
|
col_1.operator("ccic.rigutils", icon="ALIGN_LEFT", text="").param = "NLA_ALIGN_LEFT"
|
|
row = col_2.column(align=True)
|
|
row.operator("ccic.rigutils", icon="ANCHOR_LEFT", text="").param = "NLA_ALIGN_TO_LEFT"
|
|
if not active_strip:
|
|
row.enabled = False
|
|
row = col_3.column(align=True)
|
|
row.operator("ccic.rigutils", icon="ANCHOR_RIGHT", text="").param = "NLA_ALIGN_TO_RIGHT"
|
|
if not active_strip:
|
|
row.enabled = False
|
|
col_4.operator("ccic.rigutils", icon="ALIGN_RIGHT", text="").param = "NLA_ALIGN_RIGHT"
|
|
|
|
col_1.operator("ccic.rigutils", icon="FULLSCREEN_EXIT", text="").param = "NLA_SIZE_SHORTEST"
|
|
col_2.operator("ccic.rigutils", icon="FULLSCREEN_ENTER", text="").param = "NLA_SIZE_LONGEST"
|
|
row = col_3.column(align=True)
|
|
row.operator("ccic.rigutils", icon="SNAP_MIDPOINT", text="").param = "NLA_SIZE_TO"
|
|
if not active_strip:
|
|
row.enabled = False
|
|
col_4.operator("ccic.rigutils", icon="FIXED_SIZE", text="").param = "NLA_RESET_SIZE"
|
|
if not bpy.context.selected_nla_strips:
|
|
split.enabled = False
|
|
|
|
if not chr_cache:
|
|
split.enabled = False
|
|
|
|
|
|
class CCICAnimationToolsPanel(bpy.types.Panel):
|
|
bl_idname = "CCIC_PT_Animation_Tools_Panel"
|
|
bl_label = "Animation Tools"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = LINK_TAB_NAME
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
layout.label(text="Import Options:")
|
|
row = layout.row(align=True)
|
|
label = rigutils.CCICActionImportOptions.label(context)
|
|
row.operator("ccic.action_import_options", icon="COLLAPSEMENU", text=label)
|
|
|
|
column = layout.column()
|
|
|
|
motion_set_ui(column, chr_cache)
|
|
|
|
|
|
class CCICNLASetsPanel(bpy.types.Panel):
|
|
bl_idname = "CCIC_PT_NLA_Sets_Panel"
|
|
bl_label = "NLA Motion Sets"
|
|
bl_space_type = "NLA_EDITOR"
|
|
bl_region_type = "UI"
|
|
bl_category = "CC/iC"
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
motion_set_ui(layout, chr_cache, show_nla=True)
|
|
|
|
|
|
class CCICNLABakePanel(bpy.types.Panel):
|
|
bl_idname = "CCIC_PT_NLA_Bake_Panel"
|
|
bl_label = "NLA Bake"
|
|
bl_space_type = "NLA_EDITOR"
|
|
bl_region_type = "UI"
|
|
bl_category = "CC/iC"
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
col = layout.column(align=True)
|
|
row = col.row(align=True)
|
|
row.prop(prefs, "rigify_bake_nla_fk_ik", expand=True)
|
|
row.prop(prefs, "rigify_bake_shape_keys", text="", toggle=True, icon="KEYINGSET")
|
|
row = col.row()
|
|
row.scale_y = 2
|
|
row.operator("cc3.rigifier", icon="ANIM_DATA", text="Bake NLA").param = "NLA_CC_BAKE"
|
|
#row.enabled = chr_cache.rig_retarget_rig is None
|
|
|
|
col.separator()
|
|
|
|
# NLA bake motion prefix
|
|
row = col.row()
|
|
split = row.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.column().label(text="Motion Prefix")
|
|
col_2.column().prop(props, "rigify_bake_motion_prefix", text="")
|
|
col_1.column().label(text="Motion Name")
|
|
col_2.column().prop(props, "rigify_bake_motion_name", text="")
|
|
|
|
if not chr_cache:
|
|
col.enabled = False
|
|
|
|
|
|
class CC3SpringControlPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_SpringControl_Panel"
|
|
bl_label = "Spring Rig Properties"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = "Item"
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
layout = self.layout
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
if not chr_cache or not chr_cache.rigified: return
|
|
arm = chr_cache.get_armature()
|
|
if not arm: return
|
|
|
|
if not springbones.has_spring_rigs(chr_cache, arm): return
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
#box = layout.box()
|
|
#box.label(text="Spring Rig Layers", icon="XRAY")
|
|
layout.row().label(text="Spring Rig Layers:", icon="XRAY")
|
|
row = layout.row()
|
|
if utils.B400():
|
|
if "Spring (FK)" in arm.data.collections_all:
|
|
row.prop(arm.data, "collections_all[\"Spring (FK)\"].is_visible", text="FK", toggle=True)
|
|
if "Spring (IK)" in arm.data.collections_all:
|
|
row.prop(arm.data, "collections_all[\"Spring (IK)\"].is_visible", text="IK", toggle=True)
|
|
if "Spring (Tweak)" in arm.data.collections_all:
|
|
row.prop(arm.data, "collections_all[\"Spring (Tweak)\"].is_visible", text="Tweak", toggle=True)
|
|
else:
|
|
row.prop(arm.data, "layers", index = vars.SPRING_FK_LAYER, text="FK", toggle=True)
|
|
row.prop(arm.data, "layers", index = vars.SPRING_IK_LAYER, text="IK", toggle=True)
|
|
row.prop(arm.data, "layers", index = vars.SPRING_TWEAK_LAYER, text="Tweak", toggle=True)
|
|
|
|
control_bone = None
|
|
active_pose_bone = context.active_pose_bone
|
|
single_chain_bone_name = None
|
|
chain_group_bone_name = None
|
|
if active_pose_bone:
|
|
if active_pose_bone.name.endswith("_target_ik"):
|
|
single_chain_bone_name = "MCH-" + active_pose_bone.name[:-10]
|
|
elif active_pose_bone.name.endswith("_group_ik"):
|
|
chain_group_bone_name = active_pose_bone.name[:-9]
|
|
else:
|
|
single_chain_bone_name = active_pose_bone.name
|
|
|
|
parent_mode = None
|
|
|
|
if single_chain_bone_name:
|
|
search_bone_name = utils.get_prop(active_pose_bone, "ik_root", active_pose_bone.name)
|
|
spring_rig_def, mch_root_name, parent_mode = springbones.get_spring_rig_from_child(chr_cache, arm, search_bone_name)
|
|
prefix = springbones.get_spring_rig_prefix(parent_mode)
|
|
rigid_body_sim = rigidbody.get_spring_rigid_body_system(arm, prefix)
|
|
chain_name = single_chain_bone_name
|
|
if chain_name.startswith("MCH-"):
|
|
chain_name = chain_name[4:]
|
|
if chain_name.endswith("_tweak"):
|
|
chain_name = chain_name[:-6]
|
|
if chain_name.endswith("_fk"):
|
|
chain_name = chain_name[:-3]
|
|
if chain_name.startswith("DEF-"):
|
|
chain_name = chain_name[4:]
|
|
if spring_rig_def and "IK_FK" in arm.pose.bones[mch_root_name]:
|
|
control_bone = arm.pose.bones[mch_root_name]
|
|
#box = layout.box()
|
|
#box.label(text="Spring Chain", icon="LINKED")
|
|
layout.row().label(text="Spring Chain:", icon="LINKED")
|
|
if rigid_body_sim:
|
|
layout.prop(control_bone, "[\"SIM\"]", text=f"Simulation-FK ({chain_name})", slider=True)
|
|
layout.prop(control_bone, "[\"IK_FK\"]", text=f"IK-FK ({chain_name})", slider=True)
|
|
|
|
layout.separator()
|
|
|
|
if chain_group_bone_name:
|
|
|
|
spring_rig_def, mch_root_name, parent_mode = springbones.get_spring_rig_from_child(chr_cache, arm, active_pose_bone.name)
|
|
prefix = springbones.get_spring_rig_prefix(parent_mode)
|
|
rigid_body_sim = rigidbody.get_spring_rigid_body_system(arm, prefix)
|
|
|
|
layout.row().label(text="Spring Chain Group:", icon="PROP_CON")
|
|
group_name = active_pose_bone.name
|
|
if group_name.endswith("_group_ik"):
|
|
group_name = group_name[:-9]
|
|
if group_name.startswith("DEF-"):
|
|
group_name = group_name[4:]
|
|
row = layout.row()
|
|
row.operator("cc3.rigifier", icon="BACK", text="IK").param = "SPRING_GROUP_TO_IK"
|
|
row.operator("cc3.rigifier", icon="FORWARD", text="FK").param = "SPRING_GROUP_TO_FK"
|
|
if rigid_body_sim:
|
|
row.operator("cc3.rigifier", icon="ANIM_DATA", text="SIM").param = "SPRING_GROUP_TO_SIM"
|
|
|
|
layout.row().label(text="Spring Chains:", icon="LINKED")
|
|
for child in active_pose_bone.children:
|
|
if child.name.endswith("_target_ik"):
|
|
child_chain_bone_name = child.name[:-10]
|
|
search_bone_name = utils.get_prop(child, "ik_root", child.name)
|
|
spring_rig_def, mch_root_name, parent_mode = springbones.get_spring_rig_from_child(chr_cache, arm, search_bone_name)
|
|
prefix = springbones.get_spring_rig_prefix(parent_mode)
|
|
rigid_body_sim = rigidbody.get_spring_rigid_body_system(arm, prefix)
|
|
chain_name = child_chain_bone_name
|
|
if spring_rig_def and "IK_FK" in arm.pose.bones[mch_root_name]:
|
|
control_bone = arm.pose.bones[mch_root_name]
|
|
if rigid_body_sim:
|
|
layout.prop(control_bone, "[\"SIM\"]", text=f"Simulation-FK ({chain_name})", slider=True)
|
|
layout.prop(control_bone, "[\"IK_FK\"]", text=f"IK-FK ({chain_name})", slider=True)
|
|
layout.separator()
|
|
|
|
#if chr_cache and arm and obj:
|
|
# if springbones.has_spring_rigs(chr_cache, arm):
|
|
# build_allowed = True
|
|
# rigified_spring_rig = springbones.is_rigified(chr_cache, arm, parent_mode)
|
|
# if chr_cache.rigified and not rigified_spring_rig:
|
|
# build_allowed = False
|
|
# rigid_body_sim_ui(chr_cache, arm, obj, layout, True, parent_mode, enabled=build_allowed)
|
|
|
|
|
|
def scene_panel_draw(self : bpy.types.Panel, context : bpy.types.Context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
layout = self.layout
|
|
|
|
row = layout.box().row()
|
|
row.label(text="Scene Lighting", icon="LIGHT")
|
|
row.prop(prefs, "lighting_presets_all", text="", toggle=True, icon="LAYER_ACTIVE" if prefs.lighting_presets_all else "LAYER_USED")
|
|
|
|
grid = layout.grid_flow(row_major=True, columns=2, align=True)
|
|
grid.operator("cc3.scene", icon="SHADING_SOLID", text="Matcap").param = "MATCAP"
|
|
grid.operator("cc3.scene", icon="SHADING_TEXTURE", text="Default").param = "BLENDER"
|
|
grid.operator("cc3.scene", icon="SHADING_TEXTURE", text="CC3").param = "CC3"
|
|
grid.operator("cc3.scene", icon="SHADING_TEXTURE", text="Studio_01").param = "STUDIO"
|
|
grid.operator("cc3.scene", icon="NODE_COMPOSITING", text="Outdoor").param = "PRESET_1"
|
|
grid.operator("cc3.scene", icon="NODE_COMPOSITING", text="Studio_02").param = "PRESET_2"
|
|
grid.operator("cc3.scene", icon="NODE_COMPOSITING", text="Dawn").param = "PRESET_3"
|
|
grid.operator("cc3.scene", icon="NODE_COMPOSITING", text="Aqua").param = "PRESET_4"
|
|
grid.operator("cc3.scene", icon="NODE_COMPOSITING", text="Nostalgia").param = "PRESET_5"
|
|
grid.operator("cc3.scene", icon="NODE_COMPOSITING", text="Neon").param = "PRESET_6"
|
|
if prefs.lighting_presets_all:
|
|
grid.operator("cc3.scene", icon="SHADING_TEXTURE", text="Courtyard").param = "COURTYARD"
|
|
grid.operator("cc3.scene", icon="SHADING_TEXTURE", text="Interior").param = "INTERIOR"
|
|
grid.operator("cc3.scene", icon="SHADING_RENDERED", text="Aqua Dark").param = "AQUA"
|
|
grid.operator("cc3.scene", icon="SHADING_RENDERED", text="Authority").param = "AUTHORITY"
|
|
grid.operator("cc3.scene", icon="SHADING_RENDERED", text="Blur Warm").param = "BLUR_WARM"
|
|
grid.operator("cc3.scene", icon="SHADING_RENDERED", text="Exquisite").param = "EXQUISITE"
|
|
grid.operator("cc3.scene", icon="SHADING_RENDERED", text="Leading Role").param = "LEADING_ROLE"
|
|
grid.operator("cc3.scene", icon="SHADING_RENDERED", text="Neon Glow").param = "NEON"
|
|
|
|
#layout.operator("cc3.scene", icon="OUTLINER_OB_SURFACE", text="Add Backdrop").param = "BACKDROP"
|
|
|
|
world_nodes = bpy.context.scene.world.node_tree.nodes
|
|
ambient_node = nodeutils.find_node_by_type_and_keywords(world_nodes, "RGB", "rl_ambient_node")
|
|
row = layout.row(align=True)
|
|
if utils.B400():
|
|
row.prop(prefs, "lighting_use_look", expand=True)
|
|
row = layout.row(align=False)
|
|
view = context.scene.view_settings
|
|
if ambient_node:
|
|
row.prop(ambient_node.outputs[0], "default_value", text="")
|
|
row.prop(view, "look", text="")
|
|
|
|
col = layout.column(align=True)
|
|
row = col.row(align=True)
|
|
row.prop(props, "lighting_brightness", slider=True)
|
|
if props.lighting_brightness_all:
|
|
row.prop(props, "lighting_brightness_all", toggle=True, text="", icon="OUTLINER_OB_LIGHT")
|
|
else:
|
|
row.prop(props, "lighting_brightness_all", toggle=True, text="", icon="OUTLINER_DATA_LIGHT")
|
|
col.prop(props, "world_brightness", slider=True)
|
|
|
|
box = layout.box().label(text="Camera & World", icon="NODE_COMPOSITING")
|
|
|
|
grid = layout.grid_flow(row_major=True, columns=1, align=True)
|
|
grid.operator("cc3.scene", text="Targeting Camera", icon="CAMERA_DATA").param = "SETUP_CAMERA"
|
|
grid.operator("cc3.scene", text="World Setup", icon="WORLD").param = "SETUP_WORLD"
|
|
grid.operator("cc3.scene", text="Compositor Setup", icon="NODE_COMPOSITING").param = "SETUP_COMPOSITOR"
|
|
if vars.DEV:
|
|
grid.operator("cc3.scene", icon="VIEWZOOM", text="Dump Lights").param = "DUMP_SETUP"
|
|
grid.operator("cc3.scene", icon="VIEWZOOM", text="Dump Obj").param = "DUMP_OBJ"
|
|
|
|
box = layout.box().label(text="Tools", icon="TOOL_SETTINGS")
|
|
|
|
grid = layout.grid_flow(row_major=True, columns=2, align=True)
|
|
grid.operator("cc3.scene", icon="FILTER", text="Filter").param = "FILTER_LIGHTS"
|
|
grid.prop(props, "light_filter", text=f"")
|
|
grid.operator("cc3.scene", icon="GIZMO", text="Align").param = "ALIGN_WITH_VIEW"
|
|
grid.operator("cc3.scene", icon="VIEW_CAMERA", text="Add").param = "ADD_CAMERA"
|
|
|
|
#box = layout.box()
|
|
#box.label(text="Scene, World & Compositor", icon="NODE_COMPOSITING")
|
|
#column = layout.column()
|
|
#
|
|
#op = layout.operator("cc3.scene", icon="TRACKING", text="3 Point Tracking & Camera")
|
|
#op.param = "TEMPLATE"
|
|
|
|
layout.separator()
|
|
|
|
chr_cache = props.get_context_character_cache(context)
|
|
if chr_cache: # and bpy.context.scene.render.engine == 'CYCLES':
|
|
box = layout.box()
|
|
box.label(text="Renderer", icon="SHADING_RENDERED")
|
|
column = layout.column()
|
|
row = column.row()
|
|
row.alert = True
|
|
row.label(text="Invalidates Character for round trip", icon="ERROR")
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
row.operator("cc3.scene", icon="PLAY", text="Cycles Setup").param = "CYCLES_SETUP"
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
row.operator("cc3.scene", icon="PLAY", text="Eevee Setup").param = "EEVEE_SETUP"
|
|
column.separator()
|
|
|
|
cache_timeline_physics_ui(chr_cache, layout)
|
|
|
|
class CC3PipelineScenePanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_PipelineScene_Panel"
|
|
bl_label = "Scene Tools"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
scene_panel_draw(self, context)
|
|
|
|
|
|
class CC3CreateScenePanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_CreateScene_Panel"
|
|
bl_label = "Scene Tools"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
scene_panel_draw(self, context)
|
|
|
|
|
|
class CCICLinkScenePanel(bpy.types.Panel):
|
|
bl_idname = "CCIC_PT_LinkScene_Panel"
|
|
bl_label = "Scene Tools"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = LINK_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
scene_panel_draw(self, context)
|
|
|
|
|
|
class CC3ToolsCreatePanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Create_Panel"
|
|
bl_label = "Create Tools"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
layout = self.layout
|
|
|
|
chr_cache = props.get_context_character_cache(context)
|
|
chr_rig = None
|
|
if chr_cache:
|
|
chr_rig = chr_cache.get_armature()
|
|
elif context.selected_objects:
|
|
chr_rig = utils.get_armature_from_objects(context.selected_objects)
|
|
|
|
box = layout.box()
|
|
box.label(text=f"Quick Export ({vars.VERSION_STRING})", icon="EXPORT")
|
|
column = layout.column(align=True)
|
|
# export to CC3
|
|
character_export_button(chr_cache, chr_rig, column, scale=1, warn=False)
|
|
# export extras
|
|
row1 = column.row(align=True)
|
|
row1.operator("cc3.exporter", icon="MOD_CLOTH", text="Export Accessory").param = "EXPORT_ACCESSORY"
|
|
row2 = column.row(align=True)
|
|
row2.operator("cc3.exporter", icon="MESH_DATA", text="Export Replace Mesh").param = "EXPORT_MESH"
|
|
if not utils.get_active_object():
|
|
row1.enabled = False
|
|
row2.enabled = False
|
|
|
|
|
|
class CC3ToolsPhysicsPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Physics_Panel"
|
|
bl_label = "Cloth Physics"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
layout = self.layout
|
|
|
|
chr_cache = props.get_context_character_cache(context)
|
|
obj = utils.get_context_mesh(context)
|
|
obj_cache = None
|
|
proxy = None
|
|
is_proxy = False
|
|
cloth_mod = None
|
|
coll_mod = None
|
|
if chr_cache and obj:
|
|
obj, proxy, is_proxy = chr_cache.get_related_physics_objects(obj)
|
|
obj_cache = chr_cache.get_object_cache(obj)
|
|
cloth_mod = modifiers.get_cloth_physics_mod(obj)
|
|
if proxy:
|
|
coll_mod = modifiers.get_collision_physics_mod(proxy)
|
|
else:
|
|
coll_mod = modifiers.get_collision_physics_mod(obj)
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
mat = utils.get_context_material(context)
|
|
edit_mod, mix_mod = modifiers.get_material_weight_map_mods(obj, mat)
|
|
|
|
column = layout.column()
|
|
|
|
if chr_cache:
|
|
column.box().label(text="Character Physics", icon="PHYSICS")
|
|
row = column.row(align=True)
|
|
row.prop(prefs, "physics_cloth_hair", text="Hair", toggle=True)
|
|
row.prop(prefs, "physics_cloth_clothing", text="Clothing", toggle=True)
|
|
if chr_cache.physics_applied:
|
|
column.row().operator("cc3.setphysics", icon="REMOVE", text="Remove All Physics").param = "REMOVE_PHYSICS"
|
|
else:
|
|
row = column.row()
|
|
if prefs.physics_cloth_hair and prefs.physics_cloth_clothing:
|
|
text = "Apply All Physics"
|
|
elif prefs.physics_cloth_hair:
|
|
text = "Apply Hair Physics"
|
|
elif prefs.physics_cloth_clothing:
|
|
text = "Apply Cloth Physics"
|
|
else:
|
|
text = "Apply No Physics!"
|
|
row.enabled = False
|
|
row.operator("cc3.setphysics", icon="ADD", text=text).param = "APPLY_PHYSICS"
|
|
if chr_cache.physics_disabled:
|
|
row = column.row()
|
|
if prefs.physics_cloth_hair and prefs.physics_cloth_clothing:
|
|
text = "Re-enable All Physics"
|
|
elif prefs.physics_cloth_hair:
|
|
text = "Re-enable Hair Physics"
|
|
elif prefs.physics_cloth_clothing:
|
|
text = "Re-enable Cloth Physics"
|
|
else:
|
|
text = "Re-enable No Physics!"
|
|
row.enabled = False
|
|
row.operator("cc3.setphysics", icon="PLAY", text=text).param = "ENABLE_PHYSICS"
|
|
else:
|
|
column.row().operator("cc3.setphysics", icon="PAUSE", text="Disable Physics").param = "DISABLE_PHYSICS"
|
|
|
|
column.separator()
|
|
|
|
# Cloth Physics Foldout
|
|
#
|
|
|
|
if not is_proxy:
|
|
|
|
layout.box().label(text="Cloth Simulation", icon="MOD_CLOTH")
|
|
|
|
column = layout.column()
|
|
if not obj_cache:
|
|
column.enabled = False
|
|
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
if cloth_mod:
|
|
warn_icon(row, "REMOVE")
|
|
row.operator("cc3.setphysics", text="Remove Cloth Physics").param = "PHYSICS_REMOVE_CLOTH"
|
|
else:
|
|
row.operator("cc3.setphysics", icon="ADD", text="Add Cloth Physics").param = "PHYSICS_ADD_CLOTH"
|
|
|
|
column.separator()
|
|
|
|
# Cloth Physics Settings
|
|
if cloth_mod is not None:
|
|
|
|
column = layout.column()
|
|
if not obj_cache or cloth_mod is None:
|
|
column.enabled = False
|
|
|
|
# Cloth Physics Presets
|
|
sub_column = column.column(align=True)
|
|
sub_column.row().label(text="Presets", icon="PRESET")
|
|
sub_column.operator("cc3.setphysics", icon="USER", text="Hair").param = "PHYSICS_HAIR"
|
|
grid = sub_column.grid_flow(columns=2, row_major=True, align=True)
|
|
grid.operator("cc3.setphysics", icon="MATCLOTH", text="Denim").param = "PHYSICS_DENIM"
|
|
grid.operator("cc3.setphysics", icon="MATCLOTH", text="Leather").param = "PHYSICS_LEATHER"
|
|
grid.operator("cc3.setphysics", icon="MATCLOTH", text="Rubber").param = "PHYSICS_RUBBER"
|
|
grid.operator("cc3.setphysics", icon="MATCLOTH", text="Linen").param = "PHYSICS_LINEN"
|
|
grid.operator("cc3.setphysics", icon="MATCLOTH", text="Cotton").param = "PHYSICS_COTTON"
|
|
grid.operator("cc3.setphysics", icon="MATCLOTH", text="Silk").param = "PHYSICS_SILK"
|
|
|
|
|
|
column.separator()
|
|
|
|
if fake_drop_down(column.row(),
|
|
"Cloth Settings",
|
|
"section_physics_cloth_settings",
|
|
props.section_physics_cloth_settings,
|
|
icon="OPTIONS", icon_closed="OPTIONS"):
|
|
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Weight")
|
|
col_2.prop(cloth_mod.settings, "mass", text="", slider=True)
|
|
col_1.label(text="Air Damping")
|
|
col_2.prop(cloth_mod.settings, "air_damping", text="", slider=True)
|
|
col_1.label(text="Bend Resist")
|
|
col_2.prop(cloth_mod.settings, "bending_stiffness", text="", slider=True)
|
|
col_1.label(text="Pin Stiffness")
|
|
col_2.prop(cloth_mod.settings, "pin_stiffness", text="", slider=True)
|
|
col_1.label(text="Quality")
|
|
col_2.prop(cloth_mod.settings, "quality", text="", slider=True)
|
|
col_1.label(text="Collision")
|
|
col_2.prop(cloth_mod.collision_settings, "collision_quality", text="", slider=True)
|
|
col_1.label(text="Distance")
|
|
col_2.prop(cloth_mod.collision_settings, "distance_min", text="", slider=True)
|
|
col_1.label(text="Self Collision")
|
|
col_2.prop(cloth_mod.collision_settings, "use_self_collision", text="", slider=False)
|
|
if cloth_mod.collision_settings.use_self_collision:
|
|
col_1.label(text="Friction")
|
|
col_2.prop(cloth_mod.collision_settings, "self_friction", text="", slider=True)
|
|
col_1.label(text="Distance")
|
|
col_2.prop(cloth_mod.collision_settings, "self_distance_min", text="", slider=True)
|
|
|
|
column.separator()
|
|
|
|
# Cloth Collision Physics
|
|
layout.box().label(text="Cloth Collision", icon="MOD_PHYSICS")
|
|
|
|
column = layout.column()
|
|
if not obj_cache:
|
|
column.enabled = False
|
|
|
|
if obj_cache and cloth_mod is None:
|
|
if proxy:
|
|
local_view = proxy is not None and proxy.visible_get()
|
|
column.row().operator("cc3.setphysics", icon=utils.check_icon("HIDE_OFF"), text="Show Collision Proxy",
|
|
depress=local_view).param = "TOGGLE_SHOW_PROXY"
|
|
else:
|
|
grid = column.grid_flow(columns=2, align=True)
|
|
grid.prop(obj_cache, "use_collision_proxy", toggle=True, text="Use Proxy")
|
|
grid.prop(obj_cache, "collision_proxy_decimate", text="Decimate", slider=True)
|
|
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
if coll_mod:
|
|
warn_icon(row, "REMOVE")
|
|
row.operator("cc3.setphysics", text="Remove Cloth Collision").param = "PHYSICS_REMOVE_COLLISION"
|
|
else:
|
|
row.operator("cc3.setphysics", icon="ADD", text="Add Cloth Collision").param = "PHYSICS_ADD_COLLISION"
|
|
|
|
column.separator()
|
|
|
|
# Collision Physics Settings
|
|
if coll_mod is not None:
|
|
|
|
if fake_drop_down(column.row(),
|
|
"Collision Settings",
|
|
"section_physics_collision_settings",
|
|
props.section_physics_collision_settings,
|
|
icon="OPTIONS", icon_closed="OPTIONS"):
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Damping")
|
|
col_2.prop(coll_mod.settings, "damping", text="", slider=True)
|
|
col_1.label(text="Outer Thickness")
|
|
col_2.prop(coll_mod.settings, "thickness_outer", text="", slider=True)
|
|
col_1.label(text="Inner Thickness")
|
|
col_2.prop(coll_mod.settings, "thickness_inner", text="", slider=True)
|
|
col_1.label(text="Friction")
|
|
col_2.prop(coll_mod.settings, "cloth_friction", text="", slider=True)
|
|
|
|
column.separator()
|
|
|
|
# Cache
|
|
box = column.box()
|
|
box.row().label(text="Cloth Physics Cache:", icon="PREVIEW_RANGE")
|
|
|
|
has_cloth, cloth_baked, cloth_baking, cloth_point_cache = physics.cloth_physics_state(bpy.context.object)
|
|
|
|
column.row().label(text="Animation Range:")
|
|
row = column.row(align=True)
|
|
row.prop(bpy.context.scene, "use_preview_range", text="", toggle=True)
|
|
grid = row.grid_flow(columns=2, align=True)
|
|
grid.operator("cc3.scene", icon="FULLSCREEN_ENTER", text="Expand").param = "ANIM_RANGE_EXPAND"
|
|
grid.operator("cc3.scene", icon="FULLSCREEN_EXIT", text="Fit").param = "ANIM_RANGE_FIT"
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Cloth Simulation:", icon="MATCLOTH")
|
|
if bpy.context.object:
|
|
column.label(text=bpy.context.object.name, icon="OBJECT_DATA")
|
|
|
|
row = column.row()
|
|
row.operator("cc3.scene", icon=utils.check_icon("LOOP_BACK"), text="Reset Simulation").param = "PHYSICS_PREP_CLOTH"
|
|
if not has_cloth:
|
|
row.enabled = False
|
|
# frame dropping warning
|
|
if context.scene.sync_mode != "NONE":
|
|
row = column.row()
|
|
row.alert = True
|
|
row.label(text="Frame Dropping!", icon="ERROR")
|
|
#
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.context_pointer_set("point_cache", cloth_point_cache)
|
|
depress = cloth_baking
|
|
row.alert = cloth_baked
|
|
if cloth_baked:
|
|
row.operator("ptcache.free_bake", text="Free Simulation", icon="REC")
|
|
else:
|
|
row.operator("ptcache.bake", text="Bake Simulation", icon="REC", depress=cloth_baking).bake = True
|
|
if not has_cloth:
|
|
row.enabled = False
|
|
|
|
physics_all_dynamics_ui(layout)
|
|
|
|
column.separator()
|
|
|
|
# Physics Mesh Tools
|
|
layout.box().label(text="Mesh Correction", icon="MESH_DATA")
|
|
|
|
column = layout.column()
|
|
if not obj:
|
|
column.enabled = False
|
|
|
|
column.operator("cc3.setphysics", icon="MOD_EDGESPLIT", text="Fix Degenerate Mesh").param = "PHYSICS_FIX_DEGENERATE"
|
|
row = column.row()
|
|
if obj and len(obj.material_slots) < 2:
|
|
row.enabled = False
|
|
row.operator("cc3.setphysics", icon="FACE_MAPS", text="Separate Physics Materials").param = "PHYSICS_SEPARATE"
|
|
|
|
column.separator()
|
|
|
|
# Weight Maps
|
|
if not is_proxy:
|
|
|
|
layout.box().label(text="Weight Maps", icon="TEXTURE_DATA")
|
|
|
|
column = layout.column()
|
|
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
if cloth_mod is None:
|
|
col_1.enabled = False
|
|
col_2.enabled = False
|
|
col_1.label(text="WeightMap Size")
|
|
col_2.prop(props, "physics_tex_size", text="")
|
|
|
|
column = layout.column()
|
|
if not cloth_mod or not obj_cache:
|
|
column.enabled = False
|
|
|
|
weight_map = None
|
|
not_saved = False
|
|
if obj and mat:
|
|
weight_map : bpy.types.Image = physics.get_weight_map_from_modifiers(obj, mat)
|
|
if weight_map:
|
|
not_saved = weight_map.is_dirty
|
|
if weight_map:
|
|
weight_map_size = int(props.physics_tex_size)
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Current Size:")
|
|
col_2.label(text=f"{weight_map.size[0]} x {weight_map.size[1]}")
|
|
row = column.row()
|
|
row.operator("cc3.setphysics", icon="MOD_LENGTH", text="Resize Weightmap").param = "PHYSICS_RESIZE_WEIGHTMAP"
|
|
if (weight_map and
|
|
(weight_map.size[0] != weight_map_size or weight_map.size[1] != weight_map_size) and
|
|
bpy.context.mode != "PAINT_TEXTURE"):
|
|
row.enabled = True
|
|
else:
|
|
row.enabled = False
|
|
|
|
if obj is not None:
|
|
column.template_list("MATERIAL_UL_weightedmatslots", "", obj, "material_slots", obj, "active_material_index", rows=1)
|
|
if edit_mod is not None:
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Influence")
|
|
col_2.prop(mix_mod, "mask_constant", text="", slider=True)
|
|
column.separator()
|
|
if bpy.context.mode == "PAINT_TEXTURE":
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Strength")
|
|
row = col_2.row()
|
|
row.operator("cc3.setphysics", text="", icon='TRIA_LEFT').param = "PHYSICS_DEC_STRENGTH"
|
|
row.prop(props, "physics_paint_strength", text="", slider=True)
|
|
row.operator("cc3.setphysics", text="", icon='TRIA_RIGHT').param = "PHYSICS_INC_STRENGTH"
|
|
row = column.row()
|
|
row.scale_y = 2
|
|
op = row.operator("cc3.setphysics", icon="CHECKMARK", text="Done Weight Painting!")
|
|
op.param = "PHYSICS_DONE_PAINTING"
|
|
else:
|
|
if edit_mod is None:
|
|
row = column.row()
|
|
op = row.operator("cc3.setphysics", icon="ADD", text="Add Weight Map")
|
|
op.param = "PHYSICS_ADD_WEIGHTMAP"
|
|
else:
|
|
row = column.row()
|
|
op = row.operator("cc3.setphysics", icon="REMOVE", text="Remove Weight Map")
|
|
op.param = "PHYSICS_REMOVE_WEIGHTMAP"
|
|
column = layout.column()
|
|
if edit_mod is None:
|
|
column.enabled = False
|
|
op = column.operator("cc3.setphysics", icon="BRUSH_DATA", text="Paint Weight Map")
|
|
op.param = "PHYSICS_PAINT"
|
|
split = column.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
r1 = col_1.row(align=True)
|
|
r1.operator("cc3.setphysics", icon="FILEBROWSER", text="").param = "BROWSE_WEIGHTMAP"
|
|
r2 = r1.row()
|
|
r2.alert = not_saved
|
|
r2.operator("cc3.setphysics", icon="FILE_TICK", text="Save").param = "PHYSICS_SAVE"
|
|
col_2.operator("cc3.setphysics", icon="ERROR", text="Delete").param = "PHYSICS_DELETE"
|
|
|
|
|
|
class CC3ToolsSculptingPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Sculpting_Panel"
|
|
bl_label = "Sculpting"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
layout = self.layout
|
|
chr_cache = props.get_context_character_cache(context)
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
target_cache = None
|
|
if chr_cache and len(bpy.context.selected_objects) >= 2:
|
|
for obj in bpy.context.selected_objects:
|
|
target_cache = props.get_character_cache(obj, None)
|
|
if target_cache != chr_cache:
|
|
break
|
|
else:
|
|
target_cache = None
|
|
|
|
detail_body = None
|
|
sculpt_body = None
|
|
is_sculpting = False
|
|
detail_sculpting = False
|
|
body_sculpting = False
|
|
context_obj = utils.get_context_mesh(context)
|
|
if chr_cache:
|
|
sculpt_objects = chr_cache.get_sculpt_objects()
|
|
for obj in sculpt_objects:
|
|
sculpt_type = 1
|
|
if obj.name.endswith("DETAIL"):
|
|
sculpt_type = 2
|
|
if obj.visible_get():
|
|
is_sculpting = True
|
|
if context_obj == obj:
|
|
body_sculpting = body_sculpting or (sculpt_type == 1)
|
|
detail_sculpting = detail_sculpting or (sculpt_type == 2)
|
|
if sculpt_type == 1:
|
|
if context_obj == obj or chr_cache.get_sculpt_source(obj, "BODY") == context_obj:
|
|
sculpt_body = obj
|
|
elif sculpt_type == 2:
|
|
if context_obj == obj or chr_cache.get_sculpt_source(obj, "DETAIL") == context_obj:
|
|
detail_body = obj
|
|
has_body_overlay = sculpting.has_overlay_nodes(sculpt_body, sculpting.LAYER_TARGET_SCULPT)
|
|
has_detail_overlay = sculpting.has_overlay_nodes(detail_body, sculpting.LAYER_TARGET_DETAIL)
|
|
|
|
valid_character = True
|
|
if not chr_cache:
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(icon="ERROR", text="Invalid Character")
|
|
valid_character = False
|
|
elif not chr_cache.is_standard():
|
|
row = layout.row()
|
|
row.alert = True
|
|
row.label(icon="ERROR", text="Unusupported Character")
|
|
valid_character = False
|
|
|
|
## Full Body Sculpting
|
|
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
row.prop(props, "sculpt_layer_tab", expand=True)
|
|
|
|
column = layout.column()
|
|
column.enabled = valid_character
|
|
|
|
#if fake_drop_down(layout.box().row(), "Full Body Sculpting", "section_sculpt_body",
|
|
# props.section_sculpt_body, icon = "OUTLINER_OB_ARMATURE"):
|
|
if props.sculpt_layer_tab == "BODY":
|
|
|
|
if fake_drop_down(column.box().row(), "Body Sculpting", "section_sculpt_setup",
|
|
props.section_sculpt_setup,
|
|
icon="OUTLINER_OB_ARMATURE", icon_closed="OUTLINER_OB_ARMATURE"):
|
|
|
|
if not chr_cache or detail_sculpting:
|
|
column.enabled = False
|
|
|
|
row = column.row()
|
|
split = row.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text = "Multi-res Level")
|
|
col_2.prop(prefs, "sculpt_multires_level", slider=True)
|
|
if body_sculpting:
|
|
row.enabled = False
|
|
|
|
row = column.row()
|
|
row.scale_y = 2
|
|
if is_sculpting:
|
|
row.operator("cc3.sculpting", icon="PLAY_REVERSE", text="Stop Body Sculpt").param = "BODY_END"
|
|
elif sculpt_body:
|
|
row.operator("cc3.sculpting", icon="SCULPTMODE_HLT", text="Resume Body Sculpt").param = "BODY_BEGIN"
|
|
else:
|
|
row.operator("cc3.sculpting", icon="SCULPTMODE_HLT", text="Setup Body Sculpt").param = "BODY_SETUP"
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Bake Settings:")
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
split = row.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.prop(prefs, "bake_use_gpu", text="GPU", toggle=True)
|
|
col_2.prop(prefs, "body_normal_bake_size", text = "")
|
|
|
|
row = column.row(align=True)
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.sculpting", icon="RESTRICT_RENDER_OFF", text="Bake").param = "BODY_BAKE"
|
|
if chr_cache:
|
|
row.prop(chr_cache, "multires_bake_apply", text="", toggle=True, icon="MESH_ICOSPHERE")
|
|
if not sculpt_body:
|
|
row.enabled = False
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Layer Settings:")
|
|
column.separator()
|
|
|
|
grid = column.grid_flow(row_major=True, columns=2, align=True)
|
|
if chr_cache:
|
|
grid.prop(chr_cache, "body_normal_strength", text="Nrm", slider=True)
|
|
grid.prop(chr_cache, "body_ao_strength", text="AO", slider=True)
|
|
grid.prop(chr_cache, "body_mix_mode", text="")
|
|
grid.prop(chr_cache, "body_normal_definition", text="Def", slider=True)
|
|
if not has_body_overlay:
|
|
grid.enabled = False
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.sculpt_export", icon="EXPORT", text="Export Layer").param = "BODY_SKINGEN"
|
|
if not sculpt_body or not has_body_overlay:
|
|
row.enabled = False
|
|
|
|
## Detail Sculpting
|
|
|
|
#if fake_drop_down(layout.box().row(), "Detail Sculpting", "section_sculpt_detail",
|
|
# props.section_sculpt_detail, icon = "POSE_HLT"):
|
|
elif props.sculpt_layer_tab == "DETAIL":
|
|
|
|
if fake_drop_down(column.box().row(), "Detail Sculpting", "section_sculpt_setup",
|
|
props.section_sculpt_setup,
|
|
icon="MESH_MONKEY", icon_closed="MESH_MONKEY"):
|
|
|
|
if not chr_cache or body_sculpting:
|
|
column.enabled = False
|
|
|
|
row = column.row()
|
|
row.prop(prefs, "detail_sculpt_sub_target", expand=True)
|
|
if detail_body or detail_sculpting:
|
|
row.enabled = False
|
|
|
|
row = column.row()
|
|
split = row.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text = "Multi-res Level")
|
|
col_2.prop(prefs, "detail_multires_level", slider=True)
|
|
if detail_body or detail_sculpting:
|
|
row.enabled = False
|
|
|
|
row = column.row()
|
|
row.scale_y = 2.0
|
|
if is_sculpting:
|
|
row.operator("cc3.sculpting", icon="PLAY_REVERSE", text="Stop Detail Sculpt").param = "DETAIL_END"
|
|
elif detail_body:
|
|
row.operator("cc3.sculpting", icon="SCULPTMODE_HLT", text="Resume Detail Sculpt").param = "DETAIL_BEGIN"
|
|
else:
|
|
row.operator("cc3.sculpting", icon="SCULPTMODE_HLT", text="Setup Detail Sculpt").param = "DETAIL_SETUP"
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Bake Settings:")
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
split = row.split(factor=0.4)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.prop(prefs, "bake_use_gpu", text="GPU", toggle=True)
|
|
col_2.prop(prefs, "detail_normal_bake_size", text="")
|
|
|
|
row1 = column.row()
|
|
row1.scale_y = 1.5
|
|
row1.operator("cc3.sculpting", icon="RESTRICT_RENDER_OFF", text="Bake").param = "DETAIL_BAKE"
|
|
if not detail_body:
|
|
row1.enabled = False
|
|
|
|
column.separator()
|
|
|
|
column.row().label(text="Layer Settings:")
|
|
column.separator()
|
|
|
|
grid = column.grid_flow(row_major=True, columns=2, align=True)
|
|
if chr_cache:
|
|
grid.prop(chr_cache, "detail_normal_strength", text="Nrm", slider=True)
|
|
grid.prop(chr_cache, "detail_ao_strength", text="AO", slider=True)
|
|
grid.prop(chr_cache, "detail_mix_mode", text="")
|
|
grid.prop(chr_cache, "detail_normal_definition", text="Def", slider=True)
|
|
if not has_detail_overlay:
|
|
grid.enabled = False
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.sculpt_export", icon="EXPORT", text="Export Layer").param = "DETAIL_SKINGEN"
|
|
if not detail_body or not has_detail_overlay:
|
|
row.enabled = False
|
|
|
|
## Tools
|
|
|
|
if fake_drop_down(layout.box().row(), "Tools", "section_sculpt_cleanup",
|
|
props.section_sculpt_cleanup, icon = "BRUSH_DATA"):
|
|
column = layout.column()
|
|
if not chr_cache:
|
|
column.enabled = False
|
|
|
|
## Flatten Layers
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.sculpting", icon="RENDERLAYERS", text="Flatten Layers").param = "FLATTEN_LAYERS"
|
|
if not has_detail_overlay and not has_body_overlay:
|
|
row.enabled = False
|
|
|
|
## Reset / Update Base Shape
|
|
row = column.row()
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.sculpting", icon="DECORATE_OVERRIDE", text="Reset Base Shapes").param = "RESET_FROM_SOURCE"
|
|
if not sculpt_body and not detail_body:
|
|
row.enabled = False
|
|
|
|
column.label(text="Remove Sculpts:")
|
|
|
|
column.separator()
|
|
|
|
row = column.row()
|
|
warn_icon(row)
|
|
if not sculpt_body:
|
|
row.enabled = False
|
|
row.operator("cc3.sculpting", icon="TRASH", text="Remove Body Sculpt").param = "BODY_CLEAN"
|
|
|
|
row = column.row()
|
|
warn_icon(row)
|
|
if not detail_body:
|
|
row.enabled = False
|
|
row.operator("cc3.sculpting", icon="TRASH", text="Remove Detail Sculpt").param = "DETAIL_CLEAN"
|
|
|
|
column.separator()
|
|
|
|
column.label(text="Geometry Transfer:")
|
|
|
|
column.separator()
|
|
|
|
column.row().prop(props, "geom_transfer_layer", expand=True)
|
|
if props.geom_transfer_layer == "SHAPE_KEY":
|
|
column.row().prop(props, "geom_transfer_layer_name")
|
|
|
|
row = column.row()
|
|
row.operator("cc3.transfer_character", icon="OUTLINER_OB_ARMATURE", text="Transfer Character")
|
|
if not (target_cache and chr_cache):
|
|
row.enabled = False
|
|
|
|
column = layout.column()
|
|
row = column.row()
|
|
row.operator("cc3.transfer_mesh", icon="MESH_ICOSPHERE", text="Transfer Mesh")
|
|
if not utils.get_active_object() or len(bpy.context.selected_objects) < 2:
|
|
row.enabled = False
|
|
|
|
if vars.DEV:
|
|
column = layout.column()
|
|
row = column.row()
|
|
row.operator("cc3.sculpting", icon="MESH_ICOSPHERE", text="Store Lash").param = "STORE_LASH"
|
|
row = column.row()
|
|
row.operator("cc3.sculpting", icon="MESH_ICOSPHERE", text="Fix Lash").param = "FIX_LASH"
|
|
|
|
|
|
class CC3ToolsUtilityPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Utility_Panel"
|
|
bl_label = "Utilities"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
row = layout.row()
|
|
row.operator("cc3.character", icon="MATERIAL", text="Match Materials").param = "MATCH_MATERIALS"
|
|
|
|
row = layout.row()
|
|
row.operator("cc3.character", icon="KEY_DEHLT", text="Clean Empty Data").param = "CLEAN_SHAPE_KEYS"
|
|
|
|
|
|
class CCICDataLinkPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_DataLink_Panel"
|
|
bl_label = "DataLink"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = LINK_TAB_NAME
|
|
#bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
link_props = vars.link_props()
|
|
prefs = vars.prefs()
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context, strict=True)
|
|
selected_meshes = [ obj for obj in bpy.context.selected_objects if obj.type == "MESH"]
|
|
#active_chr_cache = props.get_character_cache(obj, mat)
|
|
all_valid_topography = True
|
|
if chr_cache and selected_meshes:
|
|
for obj in selected_meshes:
|
|
obj_cache = chr_cache.get_object_cache(obj)
|
|
if obj_cache:
|
|
all_valid_topography = all_valid_topography and obj_cache.validate_topography()
|
|
|
|
link_service = link.get_link_service()
|
|
actors = link_service.get_selected_actors() if link_service else None
|
|
connected = link_service and link_service.is_connected
|
|
listening = link_service and link_service.is_listening
|
|
connecting = link_service and link_service.is_connecting
|
|
working_folder = link_service.local_path if link_service else ""
|
|
is_cc = link_props.remote_app == "Character Creator"
|
|
is_iclone = link_props.remote_app == "iClone"
|
|
|
|
layout = self.layout
|
|
|
|
column = layout.column()
|
|
if prefs.datalink_bad_hostname:
|
|
column.alert=True
|
|
column.prop(prefs, "datalink_target", text="Target")
|
|
if prefs.datalink_target == "REMOTE":
|
|
column.prop(prefs, "datalink_host", text="Host")
|
|
if prefs.datalink_bad_hostname:
|
|
column.label(text="Bad Hostname")
|
|
if listening or connected:
|
|
column.enabled = False
|
|
|
|
row = layout.row()
|
|
row.prop(link_props, "link_status", text="")
|
|
row.enabled = False
|
|
|
|
column = layout.column(align=True)
|
|
row = column.row(align=True)
|
|
text = "Connect"
|
|
depressed = False
|
|
row.scale_y = 2
|
|
param = "START"
|
|
if connected:
|
|
row.alert = True
|
|
text = "Linked (Local)" if link_service.is_local() else "Linked (Remote)"
|
|
param = "DISCONNECT"
|
|
elif connecting:
|
|
row.alert = False
|
|
depressed = True
|
|
text = "Connecting..."
|
|
elif listening:
|
|
row.alert = False
|
|
depressed = True
|
|
text = "Listening..."
|
|
row.operator("ccic.datalink", icon="LINKED", text=text, depress=depressed).param = param
|
|
if connected or connecting:
|
|
row.operator("ccic.datalink", icon="X", text="").param = "STOP"
|
|
|
|
# DataLink prefs
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(), "DataLink Options", "show_data_link_prefs", props.show_data_link_prefs,
|
|
icon="PREFERENCES", icon_closed="PREFERENCES"):
|
|
split = box.split(factor=0.9)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Auto-Start Connection")
|
|
col_2.prop(prefs, "datalink_auto_start", text="")
|
|
col_1.label(text="Use Automatic Lighting")
|
|
col_2.prop(prefs, "datalink_auto_lighting", text="")
|
|
col_1.label(text="Sequence Frame Sync")
|
|
col_2.prop(prefs, "datalink_frame_sync", text="")
|
|
col_1.label(text="Preview Shape Keys")
|
|
col_2.prop(prefs, "datalink_preview_shape_keys", text="")
|
|
col_1.label(text="Match Client Rate")
|
|
col_2.prop(prefs, "datalink_match_client_rate", text="")
|
|
col_1.label(text="Retarget Prop Actions")
|
|
col_2.prop(prefs, "datalink_retarget_prop_actions", text="")
|
|
col_1.label(text="Hide Prop Bones")
|
|
col_2.prop(prefs, "datalink_hide_prop_bones", text="")
|
|
#col_1.label(text="Disable Leg Stretch")
|
|
#col_2.prop(prefs, "datalink_disable_tweak_bones", text="")
|
|
col_1.label(text="Confirm Motion")
|
|
col_2.prop(prefs, "datalink_confirm_mismatch", text="")
|
|
col_1.label(text="Confirm Replace")
|
|
col_2.prop(prefs, "datalink_confirm_replace", text="")
|
|
box.prop(prefs, "temp_folder")
|
|
box.operator("cc3.setpreferences", icon="FILE_REFRESH", text="Reset").param="RESET_DATALINK"
|
|
|
|
if True:
|
|
|
|
row = layout.row()
|
|
text = ""
|
|
if is_cc:
|
|
text = "Character Creator:"
|
|
elif is_iclone:
|
|
text = "iClone:"
|
|
else:
|
|
text = "Not connected..."
|
|
if (is_cc or is_iclone) and not connected:
|
|
text += " (Disconnected)"
|
|
if connected and working_folder:
|
|
row.label(text=text)
|
|
row.label(text=f" {working_folder}")
|
|
row.operator("ccic.datalink", icon="FILE_FOLDER", text="").param = "SHOW_PROJECT_FILES"
|
|
else:
|
|
row.label(text=text)
|
|
|
|
col = layout.column(align=True)
|
|
split = col.split(factor=0.5, align=True)
|
|
col_1 = split.column(align=True)
|
|
col_2 = split.column(align=True)
|
|
col_1.scale_y = 2.0
|
|
col_2.scale_y = 2.0
|
|
col_1.operator("ccic.datalink", icon="ARMATURE_DATA", text="Pose").param = "SEND_POSE"
|
|
if link_service and link_service.is_sequence:
|
|
col_2.operator("ccic.datalink", icon="PLAY", text="Sequence", depress=True).param = "STOP_ANIM"
|
|
else:
|
|
col_2.operator("ccic.datalink", icon="PLAY", text="Sequence").param = "SEND_ANIM"
|
|
# no pose or sequence for props yet...
|
|
if not actors:
|
|
split.enabled = False
|
|
# for now rigified Game base don't work
|
|
#if chr_cache and chr_cache.rigified and chr_cache.generation == "GameBase":
|
|
# split.enabled = False
|
|
# can't set the preview camera transform in CC4...
|
|
#grid.operator("ccic.datalink", icon="CAMERA_DATA", text="Sync Camera").param = "SYNC_CAMERA"
|
|
|
|
if is_cc:
|
|
|
|
split = col.split(factor=0.5, align=True)
|
|
col_1 = split.column(align=True)
|
|
col_2 = split.column(align=True)
|
|
row = col_1.row(align=True)
|
|
row.scale_y = 2.0
|
|
param = "SEND_REPLACE_MESH"
|
|
if not all_valid_topography:
|
|
row.alert = True
|
|
param = "SEND_REPLACE_MESH_INVALID"
|
|
op = row.operator("ccic.datalink", icon="MESH_DATA", text="Mesh").param = param
|
|
if not chr_cache or not all_valid_topography:
|
|
row.enabled = False
|
|
row = col_2.row(align=True)
|
|
row.scale_y = 2.0
|
|
row.operator("ccic.datalink", icon="TEXTURE", text="Materials").param = "SEND_MATERIAL_UPDATE"
|
|
if not chr_cache or not selected_meshes:
|
|
split.enabled = False
|
|
|
|
grid = col.grid_flow(row_major=True, columns=1, align=True)
|
|
grid.scale_y = 2.0
|
|
text = "Go CC"
|
|
param = "SEND_ACTOR"
|
|
icon = "COMMUNITY"
|
|
if chr_cache and chr_cache.is_morph():
|
|
param = "SEND_MORPH"
|
|
icon="MESH_ICOSPHERE"
|
|
#if not active_chr_cache:
|
|
# grid.enabled = False
|
|
if chr_cache and not chr_cache.is_valid_for_export():
|
|
grid.alert = True
|
|
grid.enabled = False
|
|
text = "Invalid Character!"
|
|
param += "_INVALID"
|
|
grid.operator("ccic.datalink", icon=icon, text=text).param = param
|
|
if not (chr_cache and chr_cache.can_go_cc()):
|
|
grid.enabled = False
|
|
|
|
elif is_iclone:
|
|
|
|
grid = col.grid_flow(row_major=True, columns=1, align=True)
|
|
grid.scale_y = 2.0
|
|
grid.operator("ccic.datalink", icon="COMMUNITY", text="Go iC").param = "SEND_ACTOR"
|
|
|
|
if not (chr_cache and chr_cache.can_go_ic()):
|
|
grid.enabled = False
|
|
|
|
if vars.DEV:
|
|
layout.operator("ccic.datalink", icon="ERROR", text="DEBUG").param = "DEBUG"
|
|
layout.operator("ccic.datalink", icon="ERROR", text="TEST").param = "TEST"
|
|
|
|
layout.label(text="Material Send Mode:")
|
|
row = layout.row(align=True)
|
|
row.prop(prefs, "datalink_send_mode", text=text, expand=True)
|
|
|
|
character_tools_ui(context, layout)
|
|
|
|
layout.separator()
|
|
col = layout.column(align=True)
|
|
row = col.row(align=True)
|
|
row.prop(props, "lighting_brightness", slider=True)
|
|
if props.lighting_brightness_all:
|
|
row.prop(props, "lighting_brightness_all", toggle=True, text="", icon="OUTLINER_OB_LIGHT")
|
|
else:
|
|
row.prop(props, "lighting_brightness_all", toggle=True, text="", icon="OUTLINER_DATA_LIGHT")
|
|
col.prop(props, "world_brightness", slider=True)
|
|
|
|
#if chr_cache:
|
|
#row = layout.row()
|
|
#row.operator("ccic.datalink", icon="ANIM", text="De-pivot").param = "DEPIVOT"
|
|
|
|
|
|
class CCICProportionPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Proportion_Panel"
|
|
bl_label = "Proportion Edit"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = CREATE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
layout = self.layout
|
|
chr_cache = props.get_context_character_cache(context)
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
if chr_cache:
|
|
row = layout.row()
|
|
row.scale_y = 2.0
|
|
if chr_cache.proportion_editing:
|
|
row.operator("ccic.characterproportions", icon="ARMATURE_DATA", text="Apply Proportions", depress=True).param = "END"
|
|
else:
|
|
row.operator("ccic.characterproportions", icon="ARMATURE_DATA", text="Edit Proportions").param = "BEGIN"
|
|
|
|
if chr_cache and utils.get_mode() == "POSE":
|
|
pose_bone = bpy.context.active_pose_bone
|
|
box = layout.box()
|
|
box.label(text=pose_bone.name, icon="BONE_DATA")
|
|
inherit_scale = None
|
|
for child_bone in pose_bone.children:
|
|
bone_name = child_bone.name
|
|
if "ShareBone" in bone_name or ("Twist" in bone_name and "NeckTwist" not in bone_name):
|
|
continue
|
|
if inherit_scale is None:
|
|
inherit_scale = child_bone.bone.inherit_scale
|
|
elif inherit_scale != child_bone.bone.inherit_scale:
|
|
inherit_scale = "MIXED"
|
|
break
|
|
box.label(text=f"Child Bones Inherit Scale:")
|
|
grid = box.grid_flow(row_major=True, columns=2, align=True)
|
|
grid.operator("ccic.characterproportions", text="Full", depress=True if inherit_scale=="FULL" else False).param = "INHERIT_SCALE_FULL"
|
|
grid.operator("ccic.characterproportions", text="Average", depress=True if inherit_scale=="AVERAGE" else False).param = "INHERIT_SCALE_AVERAGE"
|
|
grid.operator("ccic.characterproportions", text="Shear", depress=True if inherit_scale=="FIX_SHEAR" else False).param = "INHERIT_SCALE_FIX_SHEAR"
|
|
grid.operator("ccic.characterproportions", text="Aligned", depress=True if inherit_scale=="ALIGNED" else False).param = "INHERIT_SCALE_ALIGNED"
|
|
grid.operator("ccic.characterproportions", text="None", depress=True if inherit_scale=="NONE" else False).param = "INHERIT_SCALE_NONE"
|
|
grid.operator("ccic.characterproportions", text="Legacy", depress=True if inherit_scale=="NONE_LEGACY" else False).param = "INHERIT_SCALE_NONE_LEGACY"
|
|
row = layout.row()
|
|
row.scale_y = 1.5
|
|
row.operator("ccic.characterproportions", text="Reset All").param = "RESET"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CC3ToolsPipelineImportPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Pipeline_Import_Panel"
|
|
bl_label = "Import"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
|
|
def draw(self, context):
|
|
global debug_counter
|
|
PROPS = vars.props()
|
|
PREFS = vars.prefs()
|
|
|
|
addon_updater_ops.check_for_update_background()
|
|
if addon_updater_ops.updater.update_ready == True:
|
|
addon_updater_ops.update_notice_box_ui(self, context)
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
if chr_cache:
|
|
character_name = chr_cache.character_name
|
|
else:
|
|
character_name = "No Character"
|
|
|
|
chr_rig = None
|
|
|
|
if chr_cache:
|
|
chr_rig = chr_cache.get_armature()
|
|
else:
|
|
if context.selected_objects:
|
|
chr_rig = utils.get_armature_from_objects(context.selected_objects)
|
|
elif context.object:
|
|
chr_rig = utils.get_armature_from_object(context.object)
|
|
if chr_rig:
|
|
character_name = chr_rig.name
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
box = layout.box()
|
|
box.label(text=f"Settings ({vars.VERSION_STRING})", icon="TOOL_SETTINGS")
|
|
|
|
grid = layout.grid_flow(columns=2, align=True)
|
|
grid.prop(PROPS, "lighting_mode", toggle=True, text="Lighting")
|
|
grid.prop(PROPS, "physics_mode", toggle=True, text="Physics")
|
|
grid.prop(PROPS, "wrinkle_mode", toggle=True, text="Wrinkles")
|
|
grid.prop(PROPS, "rigify_mode", toggle=True, text="Rigify")
|
|
|
|
# Build prefs in title
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(), "Importing", "show_build_prefs", PROPS.show_build_prefs,
|
|
icon="IMPORT", icon_closed="IMPORT"):
|
|
column = box.column()
|
|
column.prop(PREFS, "import_deduplicate")
|
|
column.prop(PREFS, "import_auto_convert")
|
|
column.prop(PREFS, "import_reset_custom_normals")
|
|
column.prop(PREFS, "clean_empty_mesh_data")
|
|
column.prop(PREFS, "action_use_action_slots")
|
|
column.prop(PREFS, "action_add_key_slots_per_obj")
|
|
#column.prop(PREFS, "action_clean_actions")
|
|
column.prop(PREFS, "action_add_empty_key_channels")
|
|
column.separator()
|
|
if PREFS.import_auto_convert:
|
|
column.prop(PREFS, "auto_convert_materials")
|
|
column.prop(PREFS, "build_limit_textures")
|
|
column.prop(PREFS, "build_pack_texture_channels")
|
|
column.prop(PREFS, "build_reuse_baked_channel_packs")
|
|
column.prop(PREFS, "build_armature_edit_modifier")
|
|
column.prop(PREFS, "build_armature_preserve_volume")
|
|
column.separator()
|
|
column.label(text="Drivers:")
|
|
column.prop(PREFS, "build_shape_key_bone_drivers_jaw")
|
|
column.prop(PREFS, "build_shape_key_bone_drivers_eyes")
|
|
column.prop(PREFS, "build_shape_key_bone_drivers_head")
|
|
column.prop(PREFS, "build_body_key_drivers")
|
|
column.separator()
|
|
column.label(text="Max Texture Sizes:")
|
|
column.prop(PREFS, "use_max_tex_size")
|
|
if PREFS.use_max_tex_size:
|
|
column.prop(PREFS, "size_max_tex_default")
|
|
column.prop(PREFS, "size_max_tex_detail")
|
|
column.prop(PREFS, "size_max_tex_minimal")
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
op = row.operator("cc3.importer", icon="OUTLINER_OB_ARMATURE", text="Import Character")
|
|
op.param = "IMPORT"
|
|
row = layout.row()
|
|
row.scale_y = 2
|
|
op = row.operator("cc3.anim_importer", icon="ARMATURE_DATA", text="Import Animations")
|
|
|
|
if chr_cache and chr_cache.rigified:
|
|
export_label = "Exporting (Rigify)"
|
|
else:
|
|
export_label = "Exporting"
|
|
|
|
# reconnect and link character
|
|
reconnect_character_ui(context, layout, chr_cache)
|
|
|
|
# clean up
|
|
box = layout.box()
|
|
box.label(text="Clean Up", icon="TRASH")
|
|
|
|
box = layout.row()
|
|
box.label(text = "Character: " + character_name)
|
|
row = layout.row()
|
|
warn_icon(row)
|
|
if not chr_cache:
|
|
row.enabled = False
|
|
row.operator("cc3.importer", icon="REMOVE", text="Remove Character").param ="DELETE_CHARACTER"
|
|
|
|
|
|
class CC3ToolsPipelineExportPanel(bpy.types.Panel):
|
|
bl_idname = "CC3_PT_Pipeline_Export_Panel"
|
|
bl_label = "Export"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context):
|
|
global debug_counter
|
|
props = vars.props()
|
|
prefs = vars.prefs()
|
|
|
|
addon_updater_ops.check_for_update_background()
|
|
if addon_updater_ops.updater.update_ready == True:
|
|
addon_updater_ops.update_notice_box_ui(self, context)
|
|
|
|
chr_cache, obj, mat, obj_cache, mat_cache = utils.get_context_character(context)
|
|
|
|
if chr_cache:
|
|
character_name = chr_cache.character_name
|
|
else:
|
|
character_name = "No Character"
|
|
|
|
chr_rig = None
|
|
|
|
if chr_cache:
|
|
chr_rig = chr_cache.get_armature()
|
|
else:
|
|
if context.selected_objects:
|
|
chr_rig = utils.get_armature_from_objects(context.selected_objects)
|
|
elif context.object:
|
|
chr_rig = utils.get_armature_from_object(context.object)
|
|
if chr_rig:
|
|
character_name = chr_rig.name
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
if chr_cache and chr_cache.rigified:
|
|
export_label = "Exporting (Rigify)"
|
|
else:
|
|
export_label = "Exporting"
|
|
|
|
# export prefs in box title
|
|
box = layout.box()
|
|
if fake_drop_down(box.row(),
|
|
export_label,
|
|
"export_options",
|
|
props.export_options, icon="EXPORT", icon_closed="EXPORT"):
|
|
column = box.column()
|
|
column.prop(prefs, "export_json_changes")
|
|
column.prop(prefs, "export_texture_changes")
|
|
if prefs.export_texture_changes:
|
|
column.prop(prefs, "export_bake_nodes")
|
|
if prefs.export_bake_nodes:
|
|
column.prop(prefs, "export_bake_bump_to_normal")
|
|
column.separator()
|
|
column.label(text="Legacy Options (CC3):")
|
|
column.prop(prefs, "export_legacy_bone_roll_fix")
|
|
column.prop(prefs, "export_legacy_revert_material_names")
|
|
|
|
if chr_cache and chr_cache.rigified:
|
|
rigify_export_group(chr_cache, layout)
|
|
else:
|
|
pipeline_export_group(chr_cache, chr_rig, layout)
|
|
|
|
layout.separator()
|
|
|
|
# export extras
|
|
layout.row().operator("cc3.exporter", icon="MOD_CLOTH", text="Export Accessory").param = "EXPORT_ACCESSORY"
|
|
layout.row().operator("cc3.exporter", icon="MESH_DATA", text="Export Replace Mesh").param = "EXPORT_MESH"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#################################################
|
|
# BAKE TOOL PANELS
|
|
|
|
|
|
class CCICBakePanel(bpy.types.Panel):
|
|
bl_idname = "CCIC_PT_Bake_Panel"
|
|
bl_label = "Export Bake"
|
|
bl_space_type = "VIEW_3D"
|
|
bl_region_type = "UI"
|
|
bl_category = PIPELINE_TAB_NAME
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw_size_props(self, context, props, bake_maps, col_1, col_2):
|
|
|
|
if props.target_mode == "UNITY_HDRP":
|
|
col_1.label(text="Diffuse Size")
|
|
col_2.prop(props, "diffuse_size", text="")
|
|
col_1.label(text="Mask Size")
|
|
col_2.prop(props, "mask_size", text="")
|
|
col_1.label(text="Detail Size")
|
|
col_2.prop(props, "detail_size", text="")
|
|
col_1.label(text="Normal Size")
|
|
col_2.prop(props, "normal_size", text="")
|
|
col_1.separator()
|
|
col_2.separator()
|
|
col_1.label(text="Emission Size")
|
|
col_2.prop(props, "emissive_size", text="")
|
|
col_1.label(text="SSS Size")
|
|
col_2.prop(props, "sss_size", text="")
|
|
col_1.label(text="Transmission Size")
|
|
col_2.prop(props, "thickness_size", text="")
|
|
|
|
else:
|
|
if "Diffuse" in bake_maps:
|
|
col_1.label(text="Diffuse Size")
|
|
col_2.prop(props, "diffuse_size", text="")
|
|
if "AO" in bake_maps:
|
|
col_1.label(text="AO Size")
|
|
col_2.prop(props, "ao_size", text="")
|
|
if "Subsurface" in bake_maps:
|
|
col_1.label(text="SSS Size")
|
|
col_2.prop(props, "sss_size", text="")
|
|
if "Thickness" in bake_maps:
|
|
col_1.label(text="Thickness Size")
|
|
col_2.prop(props, "thickness_size", text="")
|
|
if "Metallic" in bake_maps:
|
|
col_1.label(text="Metallic Size")
|
|
col_2.prop(props, "metallic_size", text="")
|
|
if "Specular" in bake_maps:
|
|
col_1.label(text="Specular Size")
|
|
col_2.prop(props, "specular_size", text="")
|
|
if "Roughness" in bake_maps:
|
|
col_1.label(text="Roughness Size")
|
|
col_2.prop(props, "roughness_size", text="")
|
|
if "Emission" in bake_maps:
|
|
col_1.label(text="Emission Size")
|
|
col_2.prop(props, "emissive_size", text="")
|
|
if "Alpha" in bake_maps:
|
|
col_1.label(text="Alpha Size")
|
|
col_2.prop(props, "alpha_size", text="")
|
|
if "Transmission" in bake_maps:
|
|
col_1.label(text="Transmission Size")
|
|
col_2.prop(props, "transmission_size", text="")
|
|
if "Normal" in bake_maps:
|
|
col_1.label(text="Normal Size")
|
|
col_2.prop(props, "normal_size", text="")
|
|
if "Bump" in bake_maps:
|
|
col_1.label(text="Bump Size")
|
|
col_2.prop(props, "bump_size", text="")
|
|
if "MicroNormal" in bake_maps:
|
|
col_1.label(text="Micro Normal Size")
|
|
col_2.prop(props, "micronormal_size", text="")
|
|
if "MicroNormalMask" in bake_maps:
|
|
col_1.label(text="Micro Normal Mask Size")
|
|
col_2.prop(props, "micronormalmask_size", text="")
|
|
|
|
def draw(self, context):
|
|
props = vars.props()
|
|
bake_props = vars.bake_props()
|
|
prefs = vars.prefs()
|
|
|
|
layout = self.layout
|
|
layout.use_property_split = False
|
|
layout.use_property_decorate = False
|
|
|
|
addon_updater_ops.check_for_update_background()
|
|
if addon_updater_ops.updater.update_ready == True:
|
|
addon_updater_ops.update_notice_box_ui(self, context)
|
|
|
|
obj = context.object
|
|
mat = utils.get_context_material(context)
|
|
chr_cache = props.get_character_cache(obj, mat)
|
|
bake_cache = bake.get_export_bake_cache(mat)
|
|
|
|
if chr_cache and (chr_cache.get_render_target() != "EEVEE" or
|
|
chr_cache.is_wrinkle_active()):
|
|
box = layout.box()
|
|
box.alert = True
|
|
box.label(text="Warning:", icon="ERROR")
|
|
box.label(text="Character should be built")
|
|
box.label(text="without wrinkle maps and")
|
|
box.label(text="with Eevee materials!")
|
|
box.operator("cc3.importer", icon="NODE_MATERIAL", text="Rebuild For Eevee").param ="REBUILD_BAKE"
|
|
|
|
layout.box().label(text=f"Export Bake Settings", icon="TOOL_SETTINGS")
|
|
|
|
bake_maps = vars.get_bake_target_maps(bake_props.target_mode)
|
|
|
|
split = layout.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
col_1.label(text="Target")
|
|
col_2.prop(bake_props, "target_mode", text="", slider = True)
|
|
col_1.label(text="Bake Samples")
|
|
|
|
crow = col_2.row(align=True)
|
|
crow.prop(bake_props, "bake_samples", text="", slider = True)
|
|
crow.prop(prefs, "bake_use_gpu", text="GPU", toggle=True)
|
|
|
|
col_1.label(text="Format")
|
|
col_2.prop(bake_props, "target_format", text="", slider = True)
|
|
if bake_props.target_format == "JPEG":
|
|
col_1.label(text="JPEG Quality")
|
|
col_2.prop(bake_props, "jpeg_quality", text="", slider = True)
|
|
if bake_props.target_format == "PNG":
|
|
col_1.label(text="PNG Compression")
|
|
col_2.prop(bake_props, "png_compression", text="", slider = True)
|
|
col_1.label(text="Max Size")
|
|
col_2.prop(bake_props, "max_size", text="")
|
|
col_1.label(text="Bake Mixers")
|
|
col_2.prop(bake_props, "bake_mixers", text="")
|
|
if "Bump" in bake_maps:
|
|
col_1.label(text="Allow Bump Maps")
|
|
col_2.prop(bake_props, "allow_bump_maps", text="")
|
|
if "AO" in bake_maps:
|
|
col_1.label(text="AO in Diffuse")
|
|
col_2.prop(bake_props, "ao_in_diffuse", text="", slider = True)
|
|
if bake_props.target_mode == "UNITY_HDRP" or bake_props.target_mode == "UNITY_URP":
|
|
col_1.label(text="Smoothness Mapping")
|
|
col_2.prop(bake_props, "smoothness_mapping", text="")
|
|
if bake_props.target_mode == "GLTF":
|
|
col_1.label(text="Pack GLTF")
|
|
col_2.prop(bake_props, "pack_gltf", text="")
|
|
col_1.label(text="Bake Folder")
|
|
col_2.prop(bake_props, "bake_path", text="")
|
|
col_1.separator()
|
|
col_2.separator()
|
|
|
|
col_1.label(text="Max Sizes By Type")
|
|
col_2.prop(bake_props, "custom_sizes", text="")
|
|
|
|
disable_on_linked(layout, chr_cache)
|
|
|
|
if bake_props.custom_sizes:
|
|
|
|
layout.row().box().label(text = "Maximum Texture Sizes")
|
|
|
|
split = layout.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
|
|
self.draw_size_props(context, bake_props, bake_maps, col_1, col_2)
|
|
|
|
if obj is not None:
|
|
row = layout.row()
|
|
row.template_list("MATERIAL_UL_weightedmatslots", "", obj, "material_slots", obj, "active_material_index", rows=1)
|
|
|
|
if bake_cache and bake_cache.source_material != mat:
|
|
mat_settings = bake.get_material_bake_settings(bake_cache.source_material)
|
|
row = layout.row()
|
|
row.label(text = "(*Source Material Settings)")
|
|
else:
|
|
mat_settings = bake.get_material_bake_settings(mat)
|
|
|
|
if mat_settings is not None:
|
|
row = layout.row()
|
|
row.operator("ccic.bakesettings", icon="REMOVE", text="Remove Material Settings").param = "REMOVE"
|
|
|
|
split = layout.split(factor=0.5)
|
|
col_1 = split.column()
|
|
col_2 = split.column()
|
|
|
|
self.draw_size_props(context, mat_settings, bake_maps, col_1, col_2)
|
|
|
|
else:
|
|
row = layout.row()
|
|
row.operator("ccic.bakesettings", icon="ADD", text="Add Material Settings").param = "ADD"
|
|
|
|
if bake_cache:
|
|
if bake_cache.source_material == mat:
|
|
row = layout.row()
|
|
row.operator("ccic.bakesettings", icon="LOOP_FORWARDS", text="Restore Baked Materials").param = "BAKED"
|
|
elif bake_cache.baked_material == mat:
|
|
row = layout.row()
|
|
row.operator("ccic.bakesettings", icon="LOOP_BACK", text="Revert Source Materials").param = "SOURCE"
|
|
|
|
valid_bake_path = False
|
|
if os.path.isabs(bake_props.bake_path) or bpy.data.is_saved:
|
|
valid_bake_path = True
|
|
|
|
layout.box().label(text="Select objects to bake", icon="INFO")
|
|
|
|
col = layout.column(align=True)
|
|
row = col.row(align=True)
|
|
row.prop(prefs, "bake_objects_mode", expand=True)
|
|
row = col.row(align=True)
|
|
row.scale_y = 2
|
|
if chr_cache and chr_cache.get_render_target() != "EEVEE":
|
|
row.alert = True
|
|
|
|
# Bake Button
|
|
row.operator("ccic.baker", icon="PLAY", text="Bake").param = "BAKE"
|
|
if bake_props.target_mode == "NONE":
|
|
row.enabled = False
|
|
|
|
if not valid_bake_path:
|
|
row.enabled = False
|
|
box = col.box()
|
|
box.alert = True
|
|
box.label(text="Warning:", icon="ERROR")
|
|
box.label(text="SAVE Blend file before baking")
|
|
box.label(text="or use absolute bake path!")
|
|
|
|
if chr_cache:
|
|
if chr_cache.baked_target_mode == "GLTF":
|
|
col = layout.column(align=True)
|
|
col.label(text="Baked as GLTF")
|
|
row = col.row(align=True)
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.exporter", icon="EXPORT", text="Export GLB").param = "EXPORT_BAKED_GLB"
|
|
row = col.row(align=True)
|
|
row.scale_y = 1.5
|
|
row.operator("cc3.exporter", icon="EXPORT", text="Export GLTF").param = "EXPORT_BAKED_GLTF"
|
|
|
|
layout.separator()
|
|
|
|
layout.box().label(text="Utils", icon="INFO")
|
|
|
|
row = layout.row()
|
|
row.scale_y = 1
|
|
row.operator("ccic.jpegify", icon="PLAY", text="Jpegify")
|
|
|