3644 lines
209 KiB
Python
3644 lines
209 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, os, socket, copy
|
|
from mathutils import Vector
|
|
|
|
from . import (channel_mixer, imageutils, meshutils, sculpting, materials, rigidbody,
|
|
facerig, facerig_data, springbones, rigify_mapping_data, modifiers, nodeutils, shaders,
|
|
params, physics, basic, jsonutils, utils, vars)
|
|
from .meshutils import get_head_body_object_quick
|
|
|
|
|
|
def open_mouth_update(self, context):
|
|
props: CC3ImportProps = vars.props()
|
|
chr_cache = props.get_context_character_cache(context)
|
|
|
|
bone = utils.find_pose_bone(chr_cache, "CC_Base_JawRoot", "JawRoot")
|
|
if bone is not None:
|
|
constraint = None
|
|
|
|
for con in bone.constraints:
|
|
if "iCC3_open_mouth_contraint" in con.name:
|
|
constraint = con
|
|
|
|
if chr_cache.open_mouth == 0:
|
|
if constraint is not None:
|
|
constraint.influence = chr_cache.open_mouth
|
|
bone.constraints.remove(constraint)
|
|
else:
|
|
if constraint is None:
|
|
constraint = bone.constraints.new(type="LIMIT_ROTATION")
|
|
constraint.name = "iCC3_open_mouth_contraint"
|
|
constraint.use_limit_z = True
|
|
constraint.min_z = 0.43633
|
|
constraint.max_z = 0.43633
|
|
constraint.owner_space = "LOCAL"
|
|
constraint.influence = chr_cache.open_mouth
|
|
|
|
|
|
def eye_close_update(self, context):
|
|
props: CC3ImportProps = vars.props()
|
|
chr_cache = props.get_context_character_cache(context)
|
|
value = chr_cache.eye_close
|
|
|
|
if chr_cache:
|
|
objects = chr_cache.get_cache_objects()
|
|
BLINK_SHAPES = ["Eye_Blink", "Eye_Blink_L", "Eye_Blink_R"]
|
|
|
|
for obj in objects:
|
|
obj_cache = chr_cache.get_object_cache(obj)
|
|
if obj_cache and not obj_cache.disabled and obj_cache.is_mesh():
|
|
if (obj_cache.object_type == "BODY" or
|
|
obj_cache.object_type == "EYE_OCCLUSION" or
|
|
obj_cache.object_type == "TEARLINE"):
|
|
if obj.data.shape_keys and obj.data.shape_keys.key_blocks:
|
|
for key in BLINK_SHAPES:
|
|
if key in obj.data.shape_keys.key_blocks:
|
|
try:
|
|
obj.data.shape_keys.key_blocks[key].value = value
|
|
except:
|
|
pass
|
|
|
|
|
|
def adjust_lighting_brightness(self, context):
|
|
props = vars.props()
|
|
for light in bpy.data.objects:
|
|
if light.type == "LIGHT":
|
|
if not props.lighting_brightness_all and not utils.has_ccic_id(light):
|
|
continue
|
|
current_brightness = light.data.energy
|
|
if "rl_default_brightness" not in light.data:
|
|
light.data["rl_default_brightness"] = current_brightness
|
|
if "rl_last_brightness" not in light.data:
|
|
light.data["rl_last_brightness"] = current_brightness
|
|
last_brightness = light.data["rl_last_brightness"]
|
|
# if the brightness has been changed by the user, update the custom props
|
|
if abs(current_brightness-last_brightness) >= 0.001:
|
|
light.data["rl_default_brightness"] = current_brightness
|
|
light.data["rl_last_brightness"] = current_brightness
|
|
base_energy = light.data["rl_default_brightness"]
|
|
new_brightness = base_energy * props.lighting_brightness
|
|
light.data.energy = new_brightness
|
|
light.data["rl_last_brightness"] = new_brightness
|
|
|
|
|
|
def adjust_world_brightness(self, context):
|
|
props = vars.props()
|
|
nodes = context.scene.world.node_tree.nodes
|
|
for node in nodes:
|
|
if node.type == "BACKGROUND" and "(rl_background_node)" in node.name:
|
|
current_strength = nodeutils.extract_socket_value(node.inputs["Strength"].default_value, 1.0)
|
|
if "rl_default_strength" not in node:
|
|
node["rl_default_strength"] = current_strength
|
|
if "rl_last_strength" not in node:
|
|
node["rl_last_strength"] = current_strength
|
|
last_strength = node["rl_last_strength"]
|
|
# if the node strength has been changed by the user, update the custom props
|
|
if abs(current_strength - last_strength) >= 0.001:
|
|
node["rl_default_strength"] = current_strength
|
|
node["rl_last_strength"] = current_strength
|
|
base_strength = node["rl_default_strength"]
|
|
new_strength = base_strength * props.world_brightness
|
|
node.inputs["Strength"].default_value = new_strength
|
|
node["rl_last_strength"] = new_strength
|
|
|
|
# region Prop Updates
|
|
def update_property(self, context, prop_name, update_mode = None):
|
|
props = vars.props()
|
|
|
|
if vars.block_property_update: return
|
|
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
|
|
if chr_cache:
|
|
|
|
context_obj = context.object
|
|
context_mat = utils.get_context_material(context)
|
|
context_mat_cache = chr_cache.get_material_cache(context_mat)
|
|
linked_materials = get_linked_materials(chr_cache, context_mat, props.update_mode)
|
|
|
|
for mat_cache in linked_materials:
|
|
if mat_cache.material == context_mat:
|
|
update_shader_property(context_obj, mat_cache, prop_name)
|
|
else:
|
|
set_linked_property(prop_name, context_mat_cache, mat_cache)
|
|
update_shader_property(context_obj, mat_cache, prop_name)
|
|
|
|
# these properties will cause the eye displacement vertex group to change...
|
|
if prop_name in ["eye_iris_scale", "eye_iris_radius"]:
|
|
meshutils.rebuild_eye_vertex_groups(chr_cache)
|
|
|
|
|
|
def update_basic_property(self, context, prop_name, update_mode=None):
|
|
if vars.block_property_update: return
|
|
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
if chr_cache:
|
|
all_materials_cache = chr_cache.get_all_materials_cache()
|
|
for mat_cache in all_materials_cache:
|
|
mat = mat_cache.material
|
|
if mat:
|
|
basic.update_basic_material(mat, mat_cache, prop_name)
|
|
|
|
|
|
def update_material_property(self, context, prop_name, update_mode=None):
|
|
if vars.block_property_update: return
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
if chr_cache:
|
|
context_obj = context.object
|
|
context_mat = utils.get_context_material(context)
|
|
context_mat_cache = chr_cache.get_material_cache(context_mat)
|
|
if context_mat_cache:
|
|
try:
|
|
value = eval("context_mat_cache." + prop_name, None, locals())
|
|
context_mat["rl_" + prop_name] = value
|
|
except:
|
|
pass
|
|
|
|
|
|
def update_object_property(self, context, prop_name, update_mode=None):
|
|
if vars.block_property_update: return
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
if chr_cache:
|
|
context_obj = context.object
|
|
context_obj_cache = chr_cache.get_object_cache(context_obj)
|
|
if context_obj_cache:
|
|
try:
|
|
value = eval("context_obj_cache." + prop_name, None, locals())
|
|
context_obj["rl_" + prop_name] = value
|
|
except:
|
|
pass
|
|
|
|
|
|
def get_linked_material_types(mat_cache):
|
|
if mat_cache:
|
|
for linked in params.LINKED_MATERIALS:
|
|
if mat_cache.material_type in linked:
|
|
return linked
|
|
return []
|
|
|
|
|
|
def get_paired_material_types(mat_cache):
|
|
if mat_cache:
|
|
for paired in params.PAIRED_MATERIALS:
|
|
if mat_cache.material_type in paired:
|
|
return paired
|
|
return []
|
|
|
|
|
|
def get_linked_material_names(mat_name):
|
|
for linked in params.LINKED_MATERIAL_NAMES:
|
|
if mat_name in linked:
|
|
return linked
|
|
return []
|
|
|
|
|
|
def set_linked_property(prop_name, active_mat_cache, mat_cache):
|
|
vars.block_property_update = True
|
|
code = ""
|
|
|
|
try:
|
|
parameters = mat_cache.parameters
|
|
active_parameters = active_mat_cache.parameters
|
|
code = "parameters." + prop_name + " = active_parameters." + prop_name
|
|
exec(code, None, locals())
|
|
except Exception as e:
|
|
utils.log_error("set_linked_property(): Unable to evaluate: " + code, e)
|
|
|
|
vars.block_property_update = False
|
|
|
|
|
|
def update_shader_property(obj, mat_cache, prop_name):
|
|
props = vars.props()
|
|
|
|
if not mat_cache: return
|
|
|
|
mat = mat_cache.material
|
|
|
|
if mat and mat.node_tree:
|
|
shader_name = params.get_shader_name(mat_cache)
|
|
bsdf_node, shader_node, mix_node = nodeutils.get_shader_nodes(mat, shader_name)
|
|
shader_def = params.get_shader_def(shader_name)
|
|
|
|
if shader_def:
|
|
|
|
if "inputs" in shader_def.keys():
|
|
update_shader_input(shader_node, mat_cache, prop_name, shader_def["inputs"])
|
|
|
|
if "bsdf" in shader_def.keys():
|
|
bsdf_nodes = nodeutils.get_custom_bsdf_nodes(bsdf_node)
|
|
for bsdf_node in bsdf_nodes:
|
|
update_bsdf_input(bsdf_node, mat_cache, prop_name, shader_def["bsdf"])
|
|
|
|
if "textures" in shader_def.keys():
|
|
update_shader_tiling(shader_name, mat, mat_cache, prop_name, shader_def["textures"])
|
|
|
|
if "mapping" in shader_def.keys():
|
|
update_shader_mapping(shader_name, mat, mat_cache, prop_name, shader_def["mapping"])
|
|
|
|
if "modifiers" in shader_def.keys():
|
|
update_object_modifier(obj, mat_cache, prop_name, shader_def["modifiers"])
|
|
|
|
if "shape_keys" in shader_def.keys():
|
|
update_object_shape_keys(obj, mat_cache, prop_name, shader_def["shape_keys"])
|
|
|
|
if "settings" in shader_def.keys():
|
|
update_material_setting(mat, mat_cache, prop_name, shader_def["settings"])
|
|
|
|
else:
|
|
utils.log_error("No shader definition for: " + shader_name)
|
|
|
|
|
|
def update_shader_input(shader_node, mat_cache, prop_name, input_defs):
|
|
if shader_node:
|
|
for input_def in input_defs:
|
|
if prop_name in input_def[2:]:
|
|
nodeutils.set_node_input_value(shader_node, input_def[0], shaders.eval_input_param(input_def, mat_cache))
|
|
|
|
|
|
def update_bsdf_input(bsdf_node, mat_cache, prop_name, bsdf_defs):
|
|
if bsdf_node:
|
|
for input_def in bsdf_defs:
|
|
if prop_name in input_def[2:]:
|
|
nodeutils.set_node_input_value(bsdf_node,
|
|
input_def[0],
|
|
shaders.eval_input_param(input_def, mat_cache))
|
|
|
|
|
|
def update_shader_tiling(shader_name, mat, mat_cache, prop_name, texture_defs):
|
|
for texture_def in texture_defs:
|
|
if len(texture_def) > 5:
|
|
tiling_props = texture_def[5:]
|
|
texture_type = texture_def[2]
|
|
if prop_name in tiling_props:
|
|
tiling_node = nodeutils.get_tiling_node(mat, shader_name, texture_type)
|
|
nodeutils.set_node_input_value(tiling_node, "Tiling", shaders.eval_tiling_param(texture_def, mat_cache))
|
|
|
|
|
|
def update_shader_mapping(shader_name, mat, mat_cache, prop_name, mapping_defs):
|
|
mapping_node = None
|
|
current_texture_type = None
|
|
for mapping_def in mapping_defs:
|
|
texture_type = mapping_def[0]
|
|
if current_texture_type != texture_type:
|
|
mapping_node = nodeutils.get_tiling_node(mat, shader_name, texture_type)
|
|
current_texture_type = texture_type
|
|
if mapping_node:
|
|
tiling_props = mapping_def[3:]
|
|
if prop_name in tiling_props:
|
|
socket_name = mapping_def[1]
|
|
nodeutils.set_node_input_value(mapping_node, socket_name, shaders.eval_tiling_param(mapping_def, mat_cache, 2))
|
|
|
|
|
|
def update_object_modifier(obj, mat_cache, prop_name, mod_defs):
|
|
for mod_def in mod_defs:
|
|
if mod_def[0] == prop_name:
|
|
material_type = mod_def[1]
|
|
mod_type = mod_def[2]
|
|
mod_name = mod_def[3]
|
|
code = mod_def[4]
|
|
|
|
if mat_cache.material_type == material_type:
|
|
mod = modifiers.get_object_modifier(obj, mod_type, mod_name)
|
|
if mod:
|
|
try:
|
|
parameters = mat_cache.parameters
|
|
exec(code, None, locals())
|
|
except:
|
|
utils.log_error("update_object_modifier(): unable to execute: " + code)
|
|
|
|
|
|
def update_object_shape_keys(obj, mat_cache, prop_name, key_defs):
|
|
if utils.object_has_shape_keys(obj):
|
|
parameters = mat_cache.parameters
|
|
for key_def in key_defs:
|
|
key_name = key_def[0]
|
|
material_type = key_def[1]
|
|
parameter = key_def[2]
|
|
if mat_cache.material_type == material_type:
|
|
if prop_name == parameter:
|
|
if key_name in obj.data.shape_keys.key_blocks:
|
|
try:
|
|
code = f"parameters.{prop_name}"
|
|
value = eval(code, None, locals())
|
|
obj.data.shape_keys.key_blocks[key_name].value = value
|
|
except:
|
|
utils.log_error("update_object_shape_keys(): unable to execute: " + code)
|
|
|
|
|
|
def update_material_setting(mat, mat_cache, prop_name, setting_defs):
|
|
# mat is used in the exec code expression so keep it!
|
|
for setting_def in setting_defs:
|
|
if setting_def[0] == prop_name:
|
|
material_type = setting_def[1]
|
|
code = setting_def[2]
|
|
|
|
if mat_cache.material_type == material_type:
|
|
try:
|
|
parameters = mat_cache.parameters
|
|
exec(code, None, locals())
|
|
except:
|
|
utils.log_error("update_material_setting(): unable to execute: " + code)
|
|
|
|
|
|
def update_wrinkle_strength_all(self, context):
|
|
if vars.block_property_update: return
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
obj = get_head_body_object_quick(chr_cache)
|
|
if obj:
|
|
value = props.wrinkle_strength
|
|
prop_name = "wrinkle_regions"
|
|
for i in range(0,13):
|
|
if prop_name in obj:
|
|
obj[prop_name][i] = value
|
|
obj.update_tag()
|
|
bpy.context.view_layer.update()
|
|
return
|
|
|
|
|
|
def update_wrinkle_curve_all(self, context):
|
|
if vars.block_property_update: return
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
obj = get_head_body_object_quick(chr_cache)
|
|
if obj:
|
|
value = props.wrinkle_curve
|
|
prop_name = "wrinkle_curves"
|
|
for i in range(0,13):
|
|
if prop_name in obj:
|
|
obj[prop_name][i] = value
|
|
obj.update_tag()
|
|
bpy.context.view_layer.update()
|
|
return
|
|
|
|
|
|
def get_linked_materials(chr_cache, context_mat, update_mode):
|
|
props = vars.props()
|
|
linked_mats = set()
|
|
if chr_cache:
|
|
context_mat_cache = chr_cache.get_material_cache(context_mat)
|
|
if context_mat and context_mat_cache:
|
|
linked_mats.add(context_mat_cache)
|
|
all_materials_cache = chr_cache.get_all_materials_cache()
|
|
# linked materials are the same material type which need to be updated
|
|
# at the same time with the same values (but only when updating as linked)
|
|
linked_types = get_linked_material_types(context_mat_cache)
|
|
# paired materials are linked materials that must *always* be updated at the same time
|
|
# regardless of updating linked or not. e.g. Eye_L, Cornea_L
|
|
paired_types = get_paired_material_types(context_mat_cache)
|
|
# linked names are linked materials of common default types (pbr/sss) that are usually linked.
|
|
linked_names = get_linked_material_names(context_mat_cache.get_base_name())
|
|
all_materials_cache = chr_cache.get_all_materials_cache()
|
|
for mat_cache in all_materials_cache:
|
|
mat = mat_cache.material
|
|
if mat:
|
|
if mat_cache.material_type in paired_types:
|
|
linked_mats.add(mat_cache)
|
|
elif update_mode == "UPDATE_LINKED":
|
|
if mat_cache.material_type in linked_types or mat_cache.get_base_name() in linked_names:
|
|
linked_mats.add(mat_cache)
|
|
return [mc for mc in linked_mats]
|
|
|
|
|
|
def reset_parameters(context = bpy.context, all=False):
|
|
props = vars.props()
|
|
chr_cache = props.get_context_character_cache(context)
|
|
chr_json = chr_cache.get_character_json()
|
|
|
|
if chr_cache:
|
|
|
|
vars.block_property_update = True
|
|
if all:
|
|
shaders.init_character_property_defaults(chr_cache, chr_json)
|
|
else:
|
|
context_mat = utils.get_context_material(context)
|
|
linked_mats = get_linked_materials(chr_cache, context_mat, props.update_mode)
|
|
if linked_mats:
|
|
mats = [mat_cache.material for mat_cache in linked_mats]
|
|
shaders.init_character_property_defaults(chr_cache, chr_json, only=mats)
|
|
basic.init_basic_default(chr_cache)
|
|
vars.block_property_update = False
|
|
|
|
update_all_properties(context)
|
|
|
|
|
|
def update_all_properties(context, update_mode = None):
|
|
if vars.block_property_update: return
|
|
|
|
utils.start_timer()
|
|
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
|
|
if chr_cache:
|
|
|
|
processed = []
|
|
|
|
for obj in chr_cache.get_cache_objects():
|
|
obj_cache = chr_cache.get_object_cache(obj)
|
|
if obj_cache and not obj_cache.disabled and obj_cache.is_mesh() and obj not in processed:
|
|
|
|
processed.append(obj)
|
|
|
|
for mat in obj.data.materials:
|
|
already_processed = mat in processed
|
|
mat_cache = chr_cache.get_material_cache(mat)
|
|
|
|
if chr_cache.setup_mode == "BASIC":
|
|
|
|
if not already_processed:
|
|
basic.update_basic_material(mat, mat_cache, "ALL")
|
|
|
|
else: # ADVANCED
|
|
|
|
shader_name = params.get_shader_name(mat_cache)
|
|
bsdf_node, shader_node, mix_node = nodeutils.get_shader_nodes(mat, shader_name)
|
|
shader_def = params.get_shader_def(shader_name)
|
|
|
|
if shader_def:
|
|
if not already_processed:
|
|
shaders.apply_prop_matrix(bsdf_node, shader_node, mat_cache, shader_name)
|
|
|
|
if "textures" in shader_def.keys():
|
|
for tex_def in shader_def["textures"]:
|
|
tiling_props = tex_def[5:]
|
|
for prop_name in tiling_props:
|
|
update_shader_property(obj, mat_cache, prop_name)
|
|
|
|
# modifiers need updating even if material already processed for split objects
|
|
if "modifiers" in shader_def.keys():
|
|
for mod_def in shader_def["modifiers"]:
|
|
prop_name = mod_def[0]
|
|
update_shader_property(obj, mat_cache, prop_name)
|
|
|
|
if "shape_keys" in shader_def.keys():
|
|
for mod_def in shader_def["shape_keys"]:
|
|
prop_name = mod_def[2]
|
|
update_shader_property(obj, mat_cache, prop_name)
|
|
|
|
if not already_processed:
|
|
if "settings" in shader_def.keys():
|
|
for mat_def in shader_def["settings"]:
|
|
prop_name = mat_def[0]
|
|
update_shader_property(obj, mat_cache, prop_name)
|
|
|
|
processed.append(mat)
|
|
|
|
if obj_cache.is_eye():
|
|
meshutils.rebuild_eye_vertex_groups(chr_cache)
|
|
|
|
utils.log_timer("update_all_properties()", "ms")
|
|
|
|
|
|
def init_material_property_defaults(obj, mat, obj_cache, mat_cache, obj_json, mat_json):
|
|
if obj and obj_cache and mat and mat_cache:
|
|
utils.log_info("Re-Initializing Material Property Defaults: " + mat.name + " (" + mat_cache.material_type + ")")
|
|
if mat_cache.is_eye():
|
|
cornea_mat, cornea_mat_cache = materials.get_cornea_mat(obj, mat, mat_cache)
|
|
mat_json = jsonutils.get_material_json(obj_json, cornea_mat)
|
|
shaders.fetch_prop_defaults(obj, mat_cache, mat_json)
|
|
|
|
# endregion
|
|
|
|
|
|
def update_sculpt_mix_node(self, context, prop_name):
|
|
if vars.block_property_update: return
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
if chr_cache:
|
|
if prop_name == "detail_normal_strength":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_DETAIL, "Normal Strength", chr_cache.detail_normal_strength * 1)
|
|
elif prop_name == "body_normal_strength":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_SCULPT, "Normal Strength", chr_cache.body_normal_strength * 1)
|
|
elif prop_name == "detail_ao_strength":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_DETAIL, "AO Strength", chr_cache.detail_ao_strength * 1)
|
|
elif prop_name == "body_ao_strength":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_SCULPT, "AO Strength", chr_cache.body_ao_strength * 1)
|
|
elif prop_name == "detail_normal_definition":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_DETAIL, "Definition", chr_cache.detail_normal_definition * 1)
|
|
elif prop_name == "body_normal_definition":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_SCULPT, "Definition", chr_cache.body_normal_definition * 1)
|
|
elif prop_name == "detail_mix_mode":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_DETAIL, "Mix Mode", (1.0 if chr_cache.detail_mix_mode == "REPLACE" else 0.0))
|
|
elif prop_name == "body_mix_mode":
|
|
sculpting.update_layer_nodes(context, chr_cache, sculpting.LAYER_TARGET_SCULPT, "Mix Mode", (1.0 if chr_cache.body_mix_mode == "REPLACE" else 0.0))
|
|
|
|
|
|
def update_rig_target(self, context):
|
|
props = vars.props()
|
|
chr_cache: CC3CharacterCache = props.get_context_character_cache(context)
|
|
if chr_cache:
|
|
if self.hair_rig_target == "CC4":
|
|
self.hair_rig_bone_length = 5.0
|
|
self.hair_rig_bind_skip_length = 0.0
|
|
self.hair_rig_bind_trunc_length = 0.5
|
|
self.hair_rig_bind_bone_radius = 11.25
|
|
self.hair_rig_bind_existing_scale = 0.0
|
|
self.hair_rig_bind_bone_count = 2
|
|
self.hair_rig_bind_bone_weight = 1.0
|
|
self.hair_rig_bind_smoothing = 5
|
|
self.hair_rig_bind_weight_curve = 0.5
|
|
self.hair_rig_bind_bone_variance = 0.85
|
|
elif self.hair_rig_target == "UNITY":
|
|
self.hair_rig_bone_length = 7.5
|
|
self.hair_rig_bind_skip_length = 7.5
|
|
self.hair_rig_bind_trunc_length = 0.5
|
|
self.hair_rig_bind_bone_radius = 11.25
|
|
self.hair_rig_bind_existing_scale = 0.1
|
|
self.hair_rig_bind_bone_count = 2
|
|
self.hair_rig_bind_bone_weight = 1.0
|
|
self.hair_rig_bind_smoothing = 5
|
|
self.hair_rig_bind_weight_curve = 0.5
|
|
self.hair_rig_bind_bone_variance = 0.85
|
|
elif self.hair_rig_target == "BLENDER":
|
|
self.hair_rig_bone_length = 7.5
|
|
self.hair_rig_bind_skip_length = 7.5/2.0
|
|
self.hair_rig_bind_trunc_length = 2.5
|
|
self.hair_rig_bind_bone_radius = 11.25
|
|
self.hair_rig_bind_existing_scale = 0.1
|
|
self.hair_rig_bind_bone_count = 2
|
|
self.hair_rig_bind_bone_weight = 1.0
|
|
self.hair_rig_bind_smoothing = 5
|
|
self.hair_rig_bind_weight_curve = 0.5
|
|
self.hair_rig_bind_bone_variance = 0.85
|
|
|
|
|
|
def update_facerig_color(self, context):
|
|
facerig.update_facerig_color(context)
|
|
|
|
|
|
def clean_collection_property(collection_prop):
|
|
"""Remove any item.disabled items from validatable objects in collection property."""
|
|
repeat = True
|
|
while repeat:
|
|
repeat = False
|
|
for item in collection_prop:
|
|
valid_func = getattr(item, "validate", None)
|
|
if callable(valid_func):
|
|
if item.disabled:
|
|
repeat = True
|
|
utils.remove_from_collection(collection_prop, item)
|
|
break
|
|
|
|
|
|
class CC3OperatorProperties(bpy.types.Operator):
|
|
"""CC3 Property Functions"""
|
|
bl_idname = "cc3.setproperties"
|
|
bl_label = "CC3 Property Functions"
|
|
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
|
|
|
param: bpy.props.StringProperty(
|
|
name = "param",
|
|
default = ""
|
|
)
|
|
|
|
def execute(self, context):
|
|
|
|
if self.param == "RESET":
|
|
reset_parameters(context, all=False)
|
|
|
|
if self.param == "RESET_ALL":
|
|
reset_parameters(context, all=True)
|
|
|
|
if self.param == "APPLY_ALL":
|
|
update_all_properties(context)
|
|
|
|
return {"FINISHED"}
|
|
|
|
@classmethod
|
|
def description(cls, context, properties):
|
|
|
|
if properties.param == "RESET":
|
|
return "Reset parameters to the defaults for this material and any linked materials if in linked mode"
|
|
return ""
|
|
|
|
#region ActionProps
|
|
class CCICActionStore(bpy.types.PropertyGroup):
|
|
store_id: bpy.props.StringProperty(default="")
|
|
object: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
object_action: bpy.props.PointerProperty(type=bpy.types.Action)
|
|
object_slot_id: bpy.props.StringProperty(default="")
|
|
object_action_valid: bpy.props.BoolProperty(default=False)
|
|
data_action: bpy.props.PointerProperty(type=bpy.types.Action)
|
|
data_slot_id: bpy.props.StringProperty(default="")
|
|
data_action_valid: bpy.props.BoolProperty(default=False)
|
|
|
|
def store(self, obj, store_id: str):
|
|
self.store_id = store_id
|
|
self.object = obj
|
|
if utils.object_has_shape_keys(obj):
|
|
action, slot = utils.safe_get_action_slot(obj.data.shape_keys)
|
|
else:
|
|
action, slot = utils.safe_get_action_slot(obj)
|
|
self.object_action = action
|
|
self.object_slot_id = slot.identifier if slot else ""
|
|
self.object_action_valid = True
|
|
if utils.object_exists_is_light(obj) or utils.object_exists_is_camera(obj):
|
|
action, slot = utils.safe_get_action_slot(obj.data)
|
|
self.data_action = action
|
|
self.data_slot_id = slot.identifier if slot else ""
|
|
self.data_action_valid = True
|
|
|
|
def restore(self):
|
|
obj = self.object
|
|
if self.object_action_valid:
|
|
if utils.object_has_shape_keys(obj):
|
|
slot = utils.find_action_slot(self.object_action, slot_id=self.object_slot_id)
|
|
utils.safe_set_action(obj.data.shape_keys, self.object_action, slot=slot)
|
|
else:
|
|
slot = utils.find_action_slot(self.object_action, slot_id=self.object_slot_id)
|
|
utils.safe_set_action(obj, self.object_action, slot=slot)
|
|
if self.data_action_valid:
|
|
if utils.object_exists_is_light(obj) or utils.object_exists_is_camera(obj):
|
|
slot = utils.find_action_slot(self.data_action, slot_id=self.data_slot_id)
|
|
utils.safe_set_action(obj.data, self.data_action, slot=slot)
|
|
|
|
|
|
class CC3ActionList(bpy.types.PropertyGroup):
|
|
action: bpy.props.PointerProperty(type=bpy.types.Armature)
|
|
action_type: bpy.props.EnumProperty(items=vars.ENUM_ACTION_TYPES, default="NONE")
|
|
armature_type: bpy.props.EnumProperty(items=vars.ENUM_ARMATURE_TYPES, default="NONE")
|
|
|
|
|
|
class CC3ArmatureList(bpy.types.PropertyGroup):
|
|
armature: bpy.props.PointerProperty(type=bpy.types.Armature)
|
|
armature_type: bpy.props.EnumProperty(items=vars.ENUM_ARMATURE_TYPES, default="NONE")
|
|
actions: bpy.props.CollectionProperty(type=CC3ActionList)
|
|
|
|
|
|
class CCIC_UI_MixItem(bpy.types.PropertyGroup):
|
|
name: bpy.props.StringProperty(default="")
|
|
weight: bpy.props.FloatProperty(default=1.0)
|
|
|
|
|
|
class CCIC_UI_MixList(bpy.types.PropertyGroup):
|
|
bones: bpy.props.CollectionProperty(type=CCIC_UI_MixItem)
|
|
|
|
|
|
class CCICActionOptions(bpy.types.PropertyGroup):
|
|
action_mode: bpy.props.EnumProperty(items=[
|
|
("NEW","New","Import actions as a new set of actions and keep the existing actions"),
|
|
("REPLACE","Replace","Import new actions to replace the existing actions"),
|
|
("BLEND","Overwrite","Import the new actions into the existing actions keeping the keyframes not overwritten by the import"),
|
|
], default="NEW", name = "Import Action Mode")
|
|
frame_mode: bpy.props.EnumProperty(items=[
|
|
("START","Start","Import keyframes into Blender starting at the start frame"),
|
|
("CURRENT","Current","Import keyframes into Blender starting at the current frame"),
|
|
("MATCH","Match","Import keyframes into Blender matching keyframes with CC/iClone \n*Note: +1 as Blender starts at frame 1*"),
|
|
], default="MATCH", name = "Import Frame Mode")
|
|
use_masking: bpy.props.BoolProperty(default=False, name="Use Bone / Shape-Key Masking",
|
|
description="Only import the keyframes from the masked bones and shape-keys")
|
|
override_global: bpy.props.BoolProperty(default=False, name="Character Motion Override:",
|
|
description="Override the global action options for this character or prop")
|
|
import_mix_bones: bpy.props.CollectionProperty(type=CCIC_UI_MixItem)
|
|
rig_mix_bones_list_index: bpy.props.IntProperty(default=-1)
|
|
import_mix_bones_list_index: bpy.props.IntProperty(default=-1)
|
|
action_store: bpy.props.CollectionProperty(type=CCICActionStore)
|
|
# some masking settings ...
|
|
# some masking presets ...
|
|
# export / import presets ...
|
|
|
|
def get_action_mode(self):
|
|
props = vars.props()
|
|
action_mode = self.action_mode if self.override_global else props.action_options.action_mode
|
|
return action_mode
|
|
|
|
def get_frame_mode(self):
|
|
props = vars.props()
|
|
frame_mode = self.frame_mode if self.override_global else props.action_options.frame_mode
|
|
return frame_mode
|
|
|
|
def get_start_frame(self, ccic_start_frame, blender_current_frame):
|
|
frame_mode = self.get_frame_mode()
|
|
frame = 1
|
|
if frame_mode == "START":
|
|
frame = 1
|
|
elif frame_mode == "CURRENT":
|
|
frame = blender_current_frame
|
|
elif frame_mode == "MATCH":
|
|
frame = ccic_start_frame
|
|
return frame
|
|
|
|
def get_sequence_frame(self, ccic_frame, ccic_start_frame, blender_current_frame):
|
|
frame_mode = self.get_frame_mode()
|
|
frame = ccic_frame
|
|
if frame_mode == "START":
|
|
frame = ccic_frame - ccic_start_frame + 1
|
|
elif frame_mode == "CURRENT":
|
|
frame = blender_current_frame + ccic_frame - ccic_start_frame
|
|
elif frame_mode == "MATCH":
|
|
frame = ccic_frame
|
|
return frame
|
|
|
|
# endregion
|
|
|
|
# region HeadParameters
|
|
class CC3HeadParameters(bpy.types.PropertyGroup):
|
|
# shader (rl_head_shader)
|
|
skin_diffuse_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 1, 1, 1), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_diffuse_color"))
|
|
skin_diffuse_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_diffuse_hue"))
|
|
skin_diffuse_brightness: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_diffuse_brightness"))
|
|
skin_diffuse_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_diffuse_saturation"))
|
|
skin_diffuse_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_diffuse_hsv_strength"))
|
|
skin_cavity_ao_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_cavity_ao_strength"))
|
|
skin_blend_overlay_strength: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_blend_overlay_strength"))
|
|
skin_ao_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_ao_strength"))
|
|
skin_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"skin_ao_power"))
|
|
skin_mouth_ao: bpy.props.FloatProperty(default=2.5, min=0, max=5, update=lambda s,c: update_property(s,c,"skin_mouth_ao"))
|
|
skin_nostril_ao: bpy.props.FloatProperty(default=2.5, min=0, max=5, update=lambda s,c: update_property(s,c,"skin_nostril_ao"))
|
|
skin_lips_ao: bpy.props.FloatProperty(default=2.5, min=0, max=5, update=lambda s,c: update_property(s,c,"skin_lips_ao"))
|
|
skin_subsurface_saturation: bpy.props.FloatProperty(default=1.5, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_subsurface_saturation"))
|
|
skin_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 0.112, 0.072, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_subsurface_falloff"))
|
|
skin_subsurface_radius: bpy.props.FloatProperty(default=1.5, min=0, max=3, update=lambda s,c: update_property(s,c,"skin_subsurface_radius"))
|
|
skin_specular_scale: bpy.props.FloatProperty(default=0.4, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_specular_scale"))
|
|
skin_roughness_power: bpy.props.FloatProperty(default=0.8, min=0.01, max=2, update=lambda s,c: update_property(s,c,"skin_roughness_power"))
|
|
skin_roughness_min: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_roughness_min"))
|
|
skin_roughness_max: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_roughness_max"))
|
|
skin_cavity_strength: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_cavity_strength"))
|
|
skin_original_roughness: bpy.props.FloatProperty(default=1.0, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_original_roughness"))
|
|
# dual specular
|
|
skin_secondary_specular_ratio: bpy.props.FloatProperty(default=0.2, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_secondary_specular_ratio"))
|
|
skin_secondary_roughness_scale: bpy.props.FloatProperty(default=0.5, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_secondary_roughness_scale"))
|
|
|
|
skin_normal_strength: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_normal_strength"))
|
|
skin_micro_normal_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1.0, update=lambda s,c: update_property(s,c,"skin_micro_normal_strength"))
|
|
skin_normal_blend_strength: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_normal_blend_strength"))
|
|
skin_unmasked_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_unmasked_scatter_scale"))
|
|
skin_nose_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_nose_scatter_scale"))
|
|
skin_mouth_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_mouth_scatter_scale"))
|
|
skin_upper_lid_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_upper_lid_scatter_scale"))
|
|
skin_inner_lid_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_inner_lid_scatter_scale"))
|
|
skin_cheek_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_cheek_scatter_scale"))
|
|
skin_forehead_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_forehead_scatter_scale"))
|
|
skin_upper_lip_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_upper_lip_scatter_scale"))
|
|
skin_chin_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_chin_scatter_scale"))
|
|
skin_ear_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_ear_scatter_scale"))
|
|
skin_neck_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_neck_scatter_scale"))
|
|
skin_subsurface_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_subsurface_scale"))
|
|
skin_micro_roughness_mod: bpy.props.FloatProperty(default=0.05, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_micro_roughness_mod"))
|
|
skin_unmasked_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_unmasked_roughness_mod"))
|
|
skin_nose_roughness_mod: bpy.props.FloatProperty(default=0.119, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_nose_roughness_mod"))
|
|
skin_mouth_roughness_mod: bpy.props.FloatProperty(default=0.034, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_mouth_roughness_mod"))
|
|
skin_upper_lid_roughness_mod: bpy.props.FloatProperty(default=-0.3, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_upper_lid_roughness_mod"))
|
|
skin_inner_lid_roughness_mod: bpy.props.FloatProperty(default=-0.574, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_inner_lid_roughness_mod"))
|
|
skin_cheek_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_cheek_roughness_mod"))
|
|
skin_forehead_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_forehead_roughness_mod"))
|
|
skin_upper_lip_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_upper_lip_roughness_mod"))
|
|
skin_chin_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_chin_roughness_mod"))
|
|
skin_ear_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_ear_roughness_mod"))
|
|
skin_neck_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_neck_roughness_mod"))
|
|
skin_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_emissive_color"))
|
|
skin_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"skin_emission_strength"))
|
|
# tiling (rl_head_shader_skin_micro_normal_tiling)
|
|
skin_micro_normal_tiling: bpy.props.FloatProperty(default=20, min=0, max=50, update=lambda s,c: update_property(s,c,"skin_micro_normal_tiling"))
|
|
skin_bump_strength: bpy.props.FloatProperty(default=0.0, min=0.0, max=2.0, update=lambda s,c: update_property(s,c,"skin_bump_strength"))
|
|
skin_displacement_strength: bpy.props.FloatProperty(default=0.0, min=-3.0, max=3.0, update=lambda s,c: update_property(s,c,"skin_displacement_strength"))
|
|
skin_displacement_base: bpy.props.FloatProperty(default=0.5, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_displacement_base"))
|
|
skin_displacement_multiplier: bpy.props.FloatProperty(default=1.0, min=0.0, max=100.0, update=lambda s,c: update_property(s,c,"skin_displacement_multiplier"))
|
|
skin_displacement_delta_scale: bpy.props.FloatProperty(default=1.0, min=0.0, max=5.0, update=lambda s,c: update_property(s,c,"skin_displacement_delta_scale"))
|
|
skin_caruncle_blend: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_caruncle_blend"))
|
|
skin_caruncle_roughness: bpy.props.FloatProperty(default=0.1, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_caruncle_roughness"))
|
|
skin_caruncle_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 0.5, 0.5, 1), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_caruncle_color"))
|
|
# endregion
|
|
|
|
# region SkinParameters
|
|
class CC3SkinParameters(bpy.types.PropertyGroup):
|
|
# shader (rl_skin_shader)
|
|
skin_diffuse_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 1, 1, 1), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_diffuse_color"))
|
|
skin_diffuse_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_diffuse_hue"))
|
|
skin_diffuse_brightness: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_diffuse_brightness"))
|
|
skin_diffuse_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_diffuse_saturation"))
|
|
skin_diffuse_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_diffuse_hsv_strength"))
|
|
skin_ao_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_ao_strength"))
|
|
skin_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"skin_ao_power"))
|
|
skin_subsurface_saturation: bpy.props.FloatProperty(default=1.5, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_subsurface_saturation"))
|
|
skin_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 0.112, 0.072, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_subsurface_falloff"))
|
|
skin_subsurface_radius: bpy.props.FloatProperty(default=1.5, min=0, max=3, update=lambda s,c: update_property(s,c,"skin_subsurface_radius"))
|
|
skin_specular_scale: bpy.props.FloatProperty(default=0.4, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_specular_scale"))
|
|
skin_roughness_power: bpy.props.FloatProperty(default=0.8, min=0.01, max=2, update=lambda s,c: update_property(s,c,"skin_roughness_power"))
|
|
skin_roughness_min: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_roughness_min"))
|
|
skin_roughness_max: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_roughness_max"))
|
|
skin_cavity_strength: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_cavity_strength"))
|
|
skin_original_roughness: bpy.props.FloatProperty(default=1.0, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_original_roughness"))
|
|
# dual specular
|
|
skin_secondary_specular_ratio: bpy.props.FloatProperty(default=0.2, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_secondary_specular_ratio"))
|
|
skin_secondary_roughness_scale: bpy.props.FloatProperty(default=0.5, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_secondary_roughness_scale"))
|
|
|
|
skin_normal_strength: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_normal_strength"))
|
|
skin_micro_normal_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"skin_micro_normal_strength"))
|
|
skin_subsurface_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_subsurface_scale"))
|
|
skin_unmasked_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_unmasked_scatter_scale"))
|
|
skin_r_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_r_scatter_scale"))
|
|
skin_g_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_g_scatter_scale"))
|
|
skin_b_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_b_scatter_scale"))
|
|
skin_a_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"skin_a_scatter_scale"))
|
|
skin_micro_roughness_mod: bpy.props.FloatProperty(default=0.05, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_micro_roughness_mod"))
|
|
skin_unmasked_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_unmasked_roughness_mod"))
|
|
skin_r_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_r_roughness_mod"))
|
|
skin_g_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_g_roughness_mod"))
|
|
skin_b_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_b_roughness_mod"))
|
|
skin_a_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"skin_a_roughness_mod"))
|
|
skin_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"skin_emissive_color"))
|
|
skin_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"skin_emission_strength"))
|
|
# tiling (rl_skin_shader_skin_micro_normal_tiling)
|
|
skin_micro_normal_tiling: bpy.props.FloatProperty(default=25, min=0, max=50, update=lambda s,c: update_property(s,c,"skin_micro_normal_tiling"))
|
|
skin_bump_strength: bpy.props.FloatProperty(default=0.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_bump_strength"))
|
|
skin_displacement_strength: bpy.props.FloatProperty(default=0.0, min=-3.0, max=3.0, update=lambda s,c: update_property(s,c,"skin_displacement_strength"))
|
|
skin_displacement_base: bpy.props.FloatProperty(default=0.5, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"skin_displacement_base"))
|
|
skin_displacement_multiplier: bpy.props.FloatProperty(default=1.0, min=0.0, max=100.0, update=lambda s,c: update_property(s,c,"skin_displacement_multiplier"))
|
|
# endregion
|
|
|
|
# region EyeParameters
|
|
class CC3EyeParameters(bpy.types.PropertyGroup):
|
|
eye_subsurface_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_subsurface_scale"))
|
|
eye_subsurface_radius: bpy.props.FloatProperty(default=5.0, min=0.1, max=5, update=lambda s,c: update_property(s,c,"eye_subsurface_radius"))
|
|
eye_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_subsurface_falloff"))
|
|
eye_cornea_specular: bpy.props.FloatProperty(default=0.8, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_cornea_specular"))
|
|
eye_iris_specular: bpy.props.FloatProperty(default=0.2, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_iris_specular"))
|
|
eye_sclera_roughness: bpy.props.FloatProperty(default=0.2, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_sclera_roughness"))
|
|
eye_iris_roughness: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_iris_roughness"))
|
|
eye_cornea_roughness: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_cornea_roughness"))
|
|
eye_ao_strength: bpy.props.FloatProperty(default=0.2, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_ao_strength"))
|
|
eye_sclera_scale: bpy.props.FloatProperty(default=1.0, min=0.25, max=2.0, update=lambda s,c: update_property(s,c,"eye_sclera_scale"))
|
|
eye_sclera_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_sclera_hue"))
|
|
eye_sclera_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_sclera_saturation"))
|
|
eye_sclera_brightness: bpy.props.FloatProperty(default=0.75, min=0, max=5, update=lambda s,c: update_property(s,c,"eye_sclera_brightness"))
|
|
eye_sclera_hsv: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_sclera_hsv"))
|
|
eye_iris_scale: bpy.props.FloatProperty(default=1.0, min=0.25, max=2.0, update=lambda s,c: update_property(s,c,"eye_iris_scale"))
|
|
eye_iris_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_iris_hue"))
|
|
eye_iris_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_iris_saturation"))
|
|
eye_iris_brightness: bpy.props.FloatProperty(default=1.0, min=0, max=5, update=lambda s,c: update_property(s,c,"eye_iris_brightness"))
|
|
eye_iris_hsv: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_iris_hsv"))
|
|
eye_iris_radius: bpy.props.FloatProperty(default=0.15, min=0.01, max=0.4, update=lambda s,c: update_property(s,c,"eye_iris_radius"))
|
|
eye_iris_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_iris_color"))
|
|
eye_sclera_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_sclera_color"))
|
|
eye_iris_inner_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_iris_inner_color"))
|
|
eye_iris_cloudy_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.0, 0.0, 0.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_iris_cloudy_color"))
|
|
eye_iris_inner_scale: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_iris_inner_scale"))
|
|
eye_iris_transmission_opacity: bpy.props.FloatProperty(default=0.85, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_iris_transmission_opacity"))
|
|
eye_limbus_width: bpy.props.FloatProperty(default=0.055, min=0.01, max=0.2, update=lambda s,c: update_property(s,c,"eye_limbus_width"))
|
|
eye_limbus_dark_scale: bpy.props.FloatProperty(default=9.0, min=0.0, max=10.0, update=lambda s,c: update_property(s,c,"eye_limbus_dark_scale"))
|
|
eye_limbus_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.0, 0.0, 0.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_limbus_color"))
|
|
eye_shadow_radius: bpy.props.FloatProperty(default=0.3, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"eye_shadow_radius"))
|
|
eye_shadow_hardness: bpy.props.FloatProperty(default=0.5, min=0.01, max=0.99, update=lambda s,c: update_property(s,c,"eye_shadow_hardness"))
|
|
eye_corner_shadow_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 0.497, 0.445, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_corner_shadow_color"))
|
|
eye_color_blend_strength: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_color_blend_strength"))
|
|
eye_sclera_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_sclera_emissive_color"))
|
|
eye_sclera_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"eye_sclera_emission_strength"))
|
|
eye_iris_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_iris_emissive_color"))
|
|
eye_iris_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"eye_iris_emission_strength"))
|
|
eye_sclera_normal_strength: bpy.props.FloatProperty(default=0.1, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_sclera_normal_strength"))
|
|
eye_sclera_normal_tiling: bpy.props.FloatProperty(default=2.0, min=0, max=10, update=lambda s,c: update_property(s,c,"eye_sclera_normal_tiling"))
|
|
# non shader properties
|
|
eye_refraction_depth: bpy.props.FloatProperty(default=1, min=0, max=5, update=lambda s,c: update_property(s,c,"eye_refraction_depth"))
|
|
eye_ior: bpy.props.FloatProperty(default=1.4, min=1.01, max=2.5, update=lambda s,c: update_property(s,c,"eye_ior"))
|
|
eye_blood_vessel_height: bpy.props.FloatProperty(default=0.5, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_blood_vessel_height"))
|
|
eye_iris_bump_height: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_iris_bump_height"))
|
|
eye_iris_depth: bpy.props.FloatProperty(default=0.45, min=0, max=1.25, update=lambda s,c: update_property(s,c,"eye_iris_depth"))
|
|
eye_pupil_scale: bpy.props.FloatProperty(default=0.8, min=0.5, max=4.0, update=lambda s,c: update_property(s,c,"eye_pupil_scale"))
|
|
eye_limbus_contrast: bpy.props.FloatProperty(default=1.0, min=0.1, max=2, update=lambda s,c: update_property(s,c,"eye_limbus_contrast"))
|
|
# shape
|
|
eye_pupil_narrow: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_pupil_narrow"))
|
|
eye_pupil_wide: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_pupil_wide"))
|
|
eye_is_left_eye: bpy.props.BoolProperty(default=False, update=lambda s,c: update_property(s,c,"eye_is_left_eye"))
|
|
# endregion
|
|
|
|
#region EyeOcclusionParameters
|
|
class CC3EyeOcclusionParameters(bpy.types.PropertyGroup):
|
|
# Eye Occlusion Basic
|
|
eye_occlusion: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion"))
|
|
eye_occlusion_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.014451, 0.001628, 0.000837, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_occlusion_color"))
|
|
eye_occlusion_blur_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 0.8, 0.8, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_color"))
|
|
eye_occlusion_hardness: bpy.props.FloatProperty(default=0.5, min=0.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_hardness"))
|
|
# Old
|
|
eye_occlusion_power: bpy.props.FloatProperty(default=1.75, min=0.1, max=4, update=lambda s,c: update_property(s,c,"eye_occlusion_power"))
|
|
eye_occlusion_top_range: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top_range"))
|
|
eye_occlusion_top_curve: bpy.props.FloatProperty(default=0.7, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_occlusion_top_curve"))
|
|
eye_occlusion_bottom_range: bpy.props.FloatProperty(default=0.335, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom_range"))
|
|
eye_occlusion_bottom_curve: bpy.props.FloatProperty(default=2.0, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom_curve"))
|
|
eye_occlusion_inner_range: bpy.props.FloatProperty(default=0.625, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_inner_range"))
|
|
eye_occlusion_outer_range: bpy.props.FloatProperty(default=0.6, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_outer_range"))
|
|
eye_occlusion_strength2: bpy.props.FloatProperty(default=0.766, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_strength2"))
|
|
eye_occlusion_top2_min: bpy.props.FloatProperty(default=0.15, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top2_min"))
|
|
eye_occlusion_top2_range: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top2_range"))
|
|
eye_occlusion_tear_duct_position: bpy.props.FloatProperty(default=0.8, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_tear_duct_position"))
|
|
eye_occlusion_tear_duct_width: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_tear_duct_width"))
|
|
eye_occlusion_blur_top_range: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_top_range"))
|
|
eye_occlusion_blur_top_curve: bpy.props.FloatProperty(default=0.7, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_top_curve"))
|
|
eye_occlusion_blur_bottom_range: bpy.props.FloatProperty(default=0.335, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_bottom_range"))
|
|
eye_occlusion_blur_bottom_curve: bpy.props.FloatProperty(default=2.0, min=0, max=2, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_bottom_curve"))
|
|
eye_occlusion_blur_inner_range: bpy.props.FloatProperty(default=0.625, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_inner_range"))
|
|
eye_occlusion_blur_outer_range: bpy.props.FloatProperty(default=0.6, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_outer_range"))
|
|
# Eye Occlusion Advanced
|
|
eye_occlusion_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_strength"))
|
|
eye_occlusion_top_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top_min"))
|
|
eye_occlusion_top_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top_max"))
|
|
eye_occlusion_top_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top_in"))
|
|
eye_occlusion_top_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_top_out"))
|
|
eye_occlusion_bottom_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom_min"))
|
|
eye_occlusion_bottom_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom_max"))
|
|
eye_occlusion_bottom_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom_in"))
|
|
eye_occlusion_bottom_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom_out"))
|
|
eye_occlusion_inner_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_inner_min"))
|
|
eye_occlusion_inner_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_inner_max"))
|
|
eye_occlusion_inner_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_inner_in"))
|
|
eye_occlusion_inner_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_inner_out"))
|
|
eye_occlusion_outer_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_outer_min"))
|
|
eye_occlusion_outer_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_outer_max"))
|
|
eye_occlusion_outer_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_outer_in"))
|
|
eye_occlusion_outer_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_outer_out"))
|
|
# Blur
|
|
eye_occlusion_blur_ior: bpy.props.FloatProperty(default=0.0, min=0.0, max=2.0, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_ior"))
|
|
eye_occlusion_blur_show: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_show"))
|
|
eye_occlusion_blur_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_strength"))
|
|
eye_occlusion_blur_top_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_top_min"))
|
|
eye_occlusion_blur_top_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_top_max"))
|
|
eye_occlusion_blur_top_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_top_in"))
|
|
eye_occlusion_blur_top_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_top_out"))
|
|
eye_occlusion_blur_bottom_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_bottom_min"))
|
|
eye_occlusion_blur_bottom_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_bottom_max"))
|
|
eye_occlusion_blur_bottom_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_bottom_in"))
|
|
eye_occlusion_blur_bottom_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_bottom_out"))
|
|
eye_occlusion_blur_inner_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_inner_min"))
|
|
eye_occlusion_blur_inner_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_inner_max"))
|
|
eye_occlusion_blur_inner_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_inner_in"))
|
|
eye_occlusion_blur_inner_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_inner_out"))
|
|
eye_occlusion_blur_outer_min: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_outer_min"))
|
|
eye_occlusion_blur_outer_max: bpy.props.FloatProperty(default=0.25, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_outer_max"))
|
|
eye_occlusion_blur_outer_in: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_outer_in"))
|
|
eye_occlusion_blur_outer_out: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"eye_occlusion_blur_outer_out"))
|
|
# Shape
|
|
eye_occlusion_inner: bpy.props.FloatProperty(default=0.0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_inner"))
|
|
eye_occlusion_outer: bpy.props.FloatProperty(default=0.0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_outer"))
|
|
eye_occlusion_top: bpy.props.FloatProperty(default=0.0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_top"))
|
|
eye_occlusion_bottom: bpy.props.FloatProperty(default=0.0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_bottom"))
|
|
eye_occlusion_displace: bpy.props.FloatProperty(default=0.0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_displace"))
|
|
eye_occlusion_bulge: bpy.props.FloatProperty(default=0.0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"eye_occlusion_bulge"))
|
|
#
|
|
eye_occlusion_edge_width: bpy.props.FloatProperty(default=0.25, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"eye_occlusion_edge_width"))
|
|
# endregion
|
|
|
|
#region Tearline Parameters
|
|
class CC3TearlineParameters(bpy.types.PropertyGroup):
|
|
# Tearline
|
|
tearline_specular: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"tearline_specular"))
|
|
tearline_glossiness: bpy.props.FloatProperty(default=0.85, min=0, max=1.0, update=lambda s,c: update_property(s,c,"tearline_glossiness"))
|
|
tearline_alpha: bpy.props.FloatProperty(default=0.05, min=0, max=1.0, update=lambda s,c: update_property(s,c,"tearline_alpha"))
|
|
tearline_roughness: bpy.props.FloatProperty(default=0.15, min=0, max=1.0, update=lambda s,c: update_property(s,c,"tearline_roughness"))
|
|
tearline_inner: bpy.props.FloatProperty(default=0, min=-0.2, max=0.2, update=lambda s,c: update_property(s,c,"tearline_inner"))
|
|
tearline_displace: bpy.props.FloatProperty(default=0.1, min=-0.2, max=0.2, update=lambda s,c: update_property(s,c,"tearline_displace"))
|
|
# new
|
|
tearline_ior: bpy.props.FloatProperty(default=1.4, min=1.0, max=3.0, update=lambda s,c: update_property(s,c,"tearline_ior"))
|
|
tearline_detail: bpy.props.FloatProperty(default=0.25, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"tearline_detail"))
|
|
tearline_tiling_u: bpy.props.FloatProperty(default=1.5, min=-10.0, max=10.0, update=lambda s,c: update_property(s,c,"tearline_tiling_u"))
|
|
tearline_tiling_v: bpy.props.FloatProperty(default=1.5, min=-10.0, max=10.0, update=lambda s,c: update_property(s,c,"tearline_tiling_v"))
|
|
tearline_normal_strength: bpy.props.FloatProperty(default=0.0, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"tearline_normal_strength"))
|
|
tearline_normal_scale: bpy.props.FloatProperty(default=1.0, min=0.0, max=5.0, update=lambda s,c: update_property(s,c,"tearline_normal_scale"))
|
|
tearline_edge_fadeout: bpy.props.FloatProperty(default=1.25, min=0.0, max=2.0, update=lambda s,c: update_property(s,c,"tearline_edge_fadeout"))
|
|
# endregion
|
|
|
|
#region TeethParameters
|
|
class CC3TeethParameters(bpy.types.PropertyGroup):
|
|
# Teeth
|
|
teeth_gums_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_gums_hue"))
|
|
teeth_gums_brightness: bpy.props.FloatProperty(default=0.9, min=0, max=5, update=lambda s,c: update_property(s,c,"teeth_gums_brightness"))
|
|
teeth_gums_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"teeth_gums_saturation"))
|
|
teeth_gums_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_gums_hsv_strength"))
|
|
teeth_teeth_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_teeth_hue"))
|
|
teeth_teeth_brightness: bpy.props.FloatProperty(default=0.7, min=0, max=5, update=lambda s,c: update_property(s,c,"teeth_teeth_brightness"))
|
|
teeth_teeth_saturation: bpy.props.FloatProperty(default=0.9, min=0, max=2, update=lambda s,c: update_property(s,c,"teeth_teeth_saturation"))
|
|
teeth_teeth_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_teeth_hsv_strength"))
|
|
teeth_front_ao: bpy.props.FloatProperty(default=1.0, min=0, max=1.5, update=lambda s,c: update_property(s,c,"teeth_front_ao"))
|
|
teeth_rear_ao: bpy.props.FloatProperty(default=0.0, min=0, max=1.5, update=lambda s,c: update_property(s,c,"teeth_rear_ao"))
|
|
teeth_ao_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_ao_strength"))
|
|
teeth_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"teeth_ao_power"))
|
|
teeth_gums_subsurface_scatter: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_gums_subsurface_scatter"))
|
|
teeth_teeth_subsurface_scatter: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_teeth_subsurface_scatter"))
|
|
teeth_subsurface_radius: bpy.props.FloatProperty(default=1.0, min=0.1, max=3, update=lambda s,c: update_property(s,c,"teeth_subsurface_radius"))
|
|
teeth_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.381, 0.198, 0.13, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"teeth_subsurface_falloff"))
|
|
teeth_front_specular: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_front_specular"))
|
|
teeth_rear_specular: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_rear_specular"))
|
|
teeth_front_roughness: bpy.props.FloatProperty(default=0.4, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_front_roughness"))
|
|
teeth_rear_roughness: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_rear_roughness"))
|
|
teeth_normal_strength: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"teeth_normal_strength"))
|
|
teeth_bump_strength: bpy.props.FloatProperty(default=0.0, min=-3.0, max=3.0, update=lambda s,c: update_property(s,c,"teeth_bump_strength"))
|
|
teeth_micro_normal_strength: bpy.props.FloatProperty(default=0.3, min=0, max=1, update=lambda s,c: update_property(s,c,"teeth_micro_normal_strength"))
|
|
teeth_micro_normal_tiling: bpy.props.FloatProperty(default=10, min=0, max=20, update=lambda s,c: update_property(s,c,"teeth_micro_normal_tiling"))
|
|
teeth_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"teeth_emissive_color"))
|
|
teeth_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"teeth_emission_strength"))
|
|
teeth_displacement_strength: bpy.props.FloatProperty(default=0.0, min=-3.0, max=3.0, update=lambda s,c: update_property(s,c,"teeth_displacement_strength"))
|
|
teeth_displacement_base: bpy.props.FloatProperty(default=0.5, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"teeth_displacement_base"))
|
|
teeth_displacement_multiplier: bpy.props.FloatProperty(default=1.0, min=0.0, max=100.0, update=lambda s,c: update_property(s,c,"teeth_displacement_multiplier"))
|
|
# endregion
|
|
|
|
#region TongueParameters
|
|
class CC3TongueParameters(bpy.types.PropertyGroup):
|
|
# Tongue
|
|
tongue_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_hue"))
|
|
tongue_brightness: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"tongue_brightness"))
|
|
tongue_saturation: bpy.props.FloatProperty(default=0.95, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_saturation"))
|
|
tongue_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_hsv_strength"))
|
|
tongue_front_ao: bpy.props.FloatProperty(default=1.0, min=0, max=1.5, update=lambda s,c: update_property(s,c,"tongue_front_ao"))
|
|
tongue_rear_ao: bpy.props.FloatProperty(default=0.0, min=0, max=1.5, update=lambda s,c: update_property(s,c,"tongue_rear_ao"))
|
|
tongue_ao_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_ao_strength"))
|
|
tongue_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"tongue_ao_power"))
|
|
tongue_subsurface_scatter: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"tongue_subsurface_scatter"))
|
|
tongue_subsurface_radius: bpy.props.FloatProperty(default=1.0, min=0.1, max=5, update=lambda s,c: update_property(s,c,"tongue_subsurface_radius"))
|
|
tongue_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 0.112, 0.072, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"tongue_subsurface_falloff"))
|
|
tongue_front_specular: bpy.props.FloatProperty(default=0.259, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_front_specular"))
|
|
tongue_rear_specular: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_rear_specular"))
|
|
tongue_front_roughness: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_front_roughness"))
|
|
tongue_rear_roughness: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_rear_roughness"))
|
|
tongue_normal_strength: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"tongue_normal_strength"))
|
|
tongue_micro_normal_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"tongue_micro_normal_strength"))
|
|
tongue_micro_normal_tiling: bpy.props.FloatProperty(default=4, min=0, max=20, update=lambda s,c: update_property(s,c,"tongue_micro_normal_tiling"))
|
|
tongue_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"tongue_emissive_color"))
|
|
tongue_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"tongue_emission_strength"))
|
|
# endregion
|
|
|
|
#region HairParameters
|
|
class CC3HairParameters(bpy.types.PropertyGroup):
|
|
# shader
|
|
hair_diffuse_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 1, 1, 1), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"hair_diffuse_color"))
|
|
hair_diffuse_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_diffuse_hue"))
|
|
hair_diffuse_brightness: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_diffuse_brightness"))
|
|
hair_diffuse_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_diffuse_saturation"))
|
|
hair_diffuse_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_diffuse_hsv_strength"))
|
|
hair_global_strength: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_global_strength"))
|
|
hair_root_color_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_root_color_strength"))
|
|
hair_end_color_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_end_color_strength"))
|
|
hair_invert_root_map: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_invert_root_map"))
|
|
hair_base_color_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_base_color_strength"))
|
|
hair_root_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.144129, 0.072272, 0.046665, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_root_color"))
|
|
hair_end_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.332452, 0.184475, 0.122139, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_end_color"))
|
|
hair_highlight_a_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.502886, 0.323143, 0.205079, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_highlight_a_color"))
|
|
hair_highlight_a_start: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_a_start"))
|
|
hair_highlight_a_mid: bpy.props.FloatProperty(default=0.2, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_a_mid"))
|
|
hair_highlight_a_end: bpy.props.FloatProperty(default=0.3, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_a_end"))
|
|
hair_highlight_a_strength: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_a_strength"))
|
|
hair_highlight_a_overlap_invert: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_a_overlap_invert"))
|
|
hair_highlight_a_overlap_end: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_a_overlap_end"))
|
|
hair_highlight_b_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 1, 1, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_highlight_b_color"))
|
|
hair_highlight_b_start: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_b_start"))
|
|
hair_highlight_b_mid: bpy.props.FloatProperty(default=0.2, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_b_mid"))
|
|
hair_highlight_b_end: bpy.props.FloatProperty(default=0.3, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_b_end"))
|
|
hair_highlight_b_strength: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_b_strength"))
|
|
hair_highlight_b_overlap_invert: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_b_overlap_invert"))
|
|
hair_highlight_b_overlap_end: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_highlight_b_overlap_end"))
|
|
hair_vertex_color_strength: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_vertex_color_strength"))
|
|
hair_vertex_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0, 0, 0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_vertex_color"))
|
|
hair_anisotropic_roughness: bpy.props.FloatProperty(default=0.0375, min=0.0, max=0.15, update=lambda s,c: update_property(s,c,"hair_anisotropic_roughness"))
|
|
hair_anisotropic_shift_min: bpy.props.FloatProperty(default=0, min=-1, max=1, update=lambda s,c: update_property(s,c,"hair_anisotropic_shift_min"))
|
|
hair_anisotropic_shift_max: bpy.props.FloatProperty(default=0, min=-1, max=1, update=lambda s,c: update_property(s,c,"hair_anisotropic_shift_max"))
|
|
hair_anisotropic: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_anisotropic"))
|
|
hair_anisotropic_strength: bpy.props.FloatProperty(default=0.8, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_anisotropic_strength"))
|
|
hair_specular_blend: bpy.props.FloatProperty(default=0.75, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_specular_blend"))
|
|
hair_anisotropic_strength2: bpy.props.FloatProperty(default=0.4, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_anisotropic_strength2"))
|
|
hair_anisotropic_strength_cycles: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_anisotropic_strength_cycles"))
|
|
hair_anisotropic_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(0.05, 0.038907, 0.0325, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_anisotropic_color"))
|
|
hair_subsurface_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_subsurface_scale"))
|
|
hair_subsurface_saturation: bpy.props.FloatProperty(default=1.5, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_subsurface_saturation"))
|
|
hair_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_subsurface_falloff"))
|
|
hair_subsurface_radius: bpy.props.FloatProperty(default=1.0, min=0.1, max=5, update=lambda s,c: update_property(s,c,"hair_subsurface_radius"))
|
|
hair_diffuse_strength: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_diffuse_strength"))
|
|
hair_ao_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_ao_strength"))
|
|
hair_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"hair_ao_power"))
|
|
hair_ao_occlude_all: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_ao_occlude_all"))
|
|
hair_blend_multiply_strength: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_blend_multiply_strength"))
|
|
hair_specular_scale: bpy.props.FloatProperty(default=0.3, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_specular_scale"))
|
|
hair_roughness_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_roughness_strength"))
|
|
hair_alpha_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_alpha_strength"))
|
|
hair_alpha_power: bpy.props.FloatProperty(default=1, min=0.01, max=2, update=lambda s,c: update_property(s,c,"hair_alpha_power"))
|
|
hair_opacity: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_opacity"))
|
|
hair_normal_strength: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"hair_normal_strength"))
|
|
hair_bump_strength: bpy.props.FloatProperty(default=1, min=-3, max=3, update=lambda s,c: update_property(s,c,"hair_bump_strength"))
|
|
hair_displacement_strength: bpy.props.FloatProperty(default=0, min=-3, max=3, update=lambda s,c: update_property(s,c,"hair_displacement_strength"))
|
|
hair_displacement_base: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_displacement_base"))
|
|
hair_displacement_multiplier: bpy.props.FloatProperty(default=1.0, min=0.0, max=100.0, update=lambda s,c: update_property(s,c,"hair_displacement_multiplier"))
|
|
hair_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"hair_emissive_color"))
|
|
hair_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"hair_emission_strength"))
|
|
# not done yet
|
|
hair_enable_color: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_enable_color"))
|
|
hair_tangent_vector: bpy.props.FloatVectorProperty(subtype="XYZ", size=3,
|
|
default=(0.0, 1.0, 0.0), min = -1.0, max = 1.0, update=lambda s,c: update_property(s,c,"hair_tangent_vector"))
|
|
hair_tangent_flip_green: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_tangent_flip_green"))
|
|
hair_specular_scale2: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"hair_specular_scale2"))
|
|
# endregion
|
|
|
|
#region PbrParameters
|
|
class CC3PBRParameters(bpy.types.PropertyGroup):
|
|
# Default
|
|
default_diffuse_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 1, 1, 1), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"default_diffuse_color"))
|
|
default_ao_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_ao_strength"))
|
|
default_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"default_ao_power"))
|
|
default_blend_multiply_strength: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_blend_multiply_strength"))
|
|
default_metallic: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_metallic"))
|
|
default_specular: bpy.props.FloatProperty(default=0.5, min=0, max=2, update=lambda s,c: update_property(s,c,"default_specular"))
|
|
default_roughness: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_roughness"))
|
|
default_specular_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_specular_strength"))
|
|
default_specular_scale: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"default_specular_scale"))
|
|
default_specular_mask: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_specular_mask"))
|
|
default_roughness_power: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"default_roughness_power"))
|
|
default_roughness_min: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_roughness_min"))
|
|
default_roughness_max: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_roughness_max"))
|
|
default_alpha_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_alpha_strength"))
|
|
default_opacity: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_opacity"))
|
|
default_normal_strength: bpy.props.FloatProperty(default=1.0, min=0.0, max=2.0, update=lambda s,c: update_property(s,c,"default_normal_strength"))
|
|
default_bump_strength: bpy.props.FloatProperty(default=0.0, min=-3.0, max=3.0, update=lambda s,c: update_property(s,c,"default_bump_strength"))
|
|
default_displacement_strength: bpy.props.FloatProperty(default=0.0, min=-3.0, max=3.0, update=lambda s,c: update_property(s,c,"default_displacement_strength"))
|
|
default_displacement_base: bpy.props.FloatProperty(default=0.5, min=0.0, max=1.0, update=lambda s,c: update_property(s,c,"default_displacement_base"))
|
|
default_displacement_multiplier: bpy.props.FloatProperty(default=1.0, min=0.0, max=100.0, update=lambda s,c: update_property(s,c,"default_displacement_multiplier"))
|
|
default_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"default_emissive_color"))
|
|
default_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"default_emission_strength"))
|
|
default_reflection_strength: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_reflection_strength"))
|
|
default_reflection_blur: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_reflection_blur"))
|
|
# endregion
|
|
|
|
# region SSSParameters
|
|
class CC3SSSParameters(bpy.types.PropertyGroup):
|
|
# Default
|
|
default_diffuse_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1, 1, 1, 1), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"default_diffuse_color"))
|
|
default_hue: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"default_hue"))
|
|
default_brightness: bpy.props.FloatProperty(default=1.0, min=0, max=2, update=lambda s,c: update_property(s,c,"default_brightness"))
|
|
default_saturation: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_saturation"))
|
|
default_hsv_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_hsv_strength"))
|
|
default_ao_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_ao_strength"))
|
|
default_ao_power: bpy.props.FloatProperty(default=1, min=0, max=8, update=lambda s,c: update_property(s,c,"default_ao_power"))
|
|
default_blend_multiply_strength: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_blend_multiply_strength"))
|
|
default_metallic: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_metallic"))
|
|
default_specular: bpy.props.FloatProperty(default=0.5, min=0, max=2, update=lambda s,c: update_property(s,c,"default_specular"))
|
|
default_roughness: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_roughness"))
|
|
default_specular_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_specular_strength"))
|
|
default_specular_scale: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"default_specular_scale"))
|
|
default_specular_mask: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_specular_mask"))
|
|
default_roughness_power: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"default_roughness_power"))
|
|
default_roughness_min: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_roughness_min"))
|
|
default_roughness_max: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_roughness_max"))
|
|
default_alpha_strength: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_alpha_strength"))
|
|
default_opacity: bpy.props.FloatProperty(default=1, min=0, max=1, update=lambda s,c: update_property(s,c,"default_opacity"))
|
|
default_normal_strength: bpy.props.FloatProperty(default=1, min=0, max=2, update=lambda s,c: update_property(s,c,"default_normal_strength"))
|
|
default_bump_strength: bpy.props.FloatProperty(default=1, min=-3, max=3, update=lambda s,c: update_property(s,c,"default_bump_strength"))
|
|
default_displacement_strength: bpy.props.FloatProperty(default=1, min=-5, max=5, update=lambda s,c: update_property(s,c,"default_displacement_strength"))
|
|
default_displacement_base: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"default_displacement_base"))
|
|
default_displacement_multiplier: bpy.props.FloatProperty(default=1.0, min=0.0, max=100.0, update=lambda s,c: update_property(s,c,"default_displacement_multiplier"))
|
|
default_emissive_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"default_emissive_color"))
|
|
default_emission_strength: bpy.props.FloatProperty(default=0, min=0, soft_max=1, max=100, update=lambda s,c: update_property(s,c,"default_emission_strength"))
|
|
default_subsurface_falloff: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1, 1, 1.0), min = 0.0, max = 1.0,
|
|
update=lambda s,c: update_property(s,c,"default_subsurface_falloff"))
|
|
default_subsurface_radius: bpy.props.FloatProperty(default=1.5, min=0, max=50, update=lambda s,c: update_property(s,c,"default_subsurface_radius"))
|
|
default_micro_normal_strength: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_property(s,c,"default_micronormal_strength"))
|
|
default_subsurface_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"default_subsurface_scale"))
|
|
default_unmasked_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"default_unmasked_scatter_scale"))
|
|
default_r_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"default_r_scatter_scale"))
|
|
default_g_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"default_g_scatter_scale"))
|
|
default_b_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"default_b_scatter_scale"))
|
|
default_a_scatter_scale: bpy.props.FloatProperty(default=1.0, min=0, max=2.0, update=lambda s,c: update_property(s,c,"default_a_scatter_scale"))
|
|
default_micro_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"default_micro_roughness_mod"))
|
|
default_unmasked_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"default_unmasked_roughness_mod"))
|
|
default_r_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"default_r_roughness_mod"))
|
|
default_g_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"default_g_roughness_mod"))
|
|
default_b_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"default_b_roughness_mod"))
|
|
default_a_roughness_mod: bpy.props.FloatProperty(default=0, min=-1.5, max=1.5, update=lambda s,c: update_property(s,c,"default_a_roughness_mod"))
|
|
default_micro_normal_tiling: bpy.props.FloatProperty(default=5, min=0, max=50, update=lambda s,c: update_property(s,c,"default_micro_normal_tiling"))
|
|
default_reflection_strength: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_reflection_strength"))
|
|
default_reflection_blur: bpy.props.FloatProperty(default=0, min=0, max=1, update=lambda s,c: update_property(s,c,"default_reflection_blur"))
|
|
# endregion
|
|
|
|
# region BasicParameters
|
|
class CC3BasicParameters(bpy.types.PropertyGroup):
|
|
eye_occlusion: bpy.props.FloatProperty(default=0.5, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"eye_occlusion"))
|
|
eye_occlusion_power: bpy.props.FloatProperty(default=0.5, min=0.5, max=1.5, update=lambda s,c: update_basic_property(s,c,"eye_occlusion_power"))
|
|
eye_brightness: bpy.props.FloatProperty(default=0.9, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"eye_brightness"))
|
|
eye_specular: bpy.props.FloatProperty(default=0.8, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"eye_specular"))
|
|
eye_roughness: bpy.props.FloatProperty(default=0.05, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"eye_roughness"))
|
|
eye_normal: bpy.props.FloatProperty(default=0.1, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"eye_normal"))
|
|
skin_ao: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"skin_ao"))
|
|
hair_ao: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"hair_ao"))
|
|
default_ao: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"default_ao"))
|
|
default_specular: bpy.props.FloatProperty(default=0.5, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"default_specular"))
|
|
skin_specular: bpy.props.FloatProperty(default=0.4, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"skin_specular"))
|
|
hair_specular: bpy.props.FloatProperty(default=0.5, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"hair_specular"))
|
|
scalp_specular: bpy.props.FloatProperty(default=0.0, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"scalp_specular"))
|
|
teeth_specular: bpy.props.FloatProperty(default=0.25, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"teeth_specular"))
|
|
tongue_specular: bpy.props.FloatProperty(default=0.259, min=0, max=2, update=lambda s,c: update_basic_property(s,c,"tongue_specular"))
|
|
skin_roughness: bpy.props.FloatProperty(default=0.15, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"skin_roughness"))
|
|
teeth_roughness: bpy.props.FloatProperty(default=0.4, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"teeth_roughness"))
|
|
tongue_roughness: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=lambda s,c: update_basic_property(s,c,"tongue_roughness"))
|
|
hair_bump: bpy.props.FloatProperty(default=1, min=0, max=10, update=lambda s,c: update_basic_property(s,c,"hair_bump"))
|
|
default_bump: bpy.props.FloatProperty(default=5, min=0, max=10, update=lambda s,c: update_basic_property(s,c,"default_bump"))
|
|
tearline_alpha: bpy.props.FloatProperty(default=0.05, min=0, max=0.2, update=lambda s,c: update_basic_property(s,c,"tearline_alpha"))
|
|
tearline_roughness: bpy.props.FloatProperty(default=0.15, min=0, max=0.5, update=lambda s,c: update_basic_property(s,c,"tearline_roughness"))
|
|
|
|
|
|
class CC3TextureMapping(bpy.types.PropertyGroup):
|
|
texture_type: bpy.props.StringProperty(default="DIFFUSE")
|
|
texture_path: bpy.props.StringProperty(default="")
|
|
embedded: bpy.props.BoolProperty(default=False)
|
|
image: bpy.props.PointerProperty(type=bpy.types.Image)
|
|
strength: bpy.props.FloatProperty(default=1.0)
|
|
location: bpy.props.FloatVectorProperty(subtype="TRANSLATION", size=3, default=(0.0, 0.0, 0.0))
|
|
rotation: bpy.props.FloatVectorProperty(subtype="EULER", size=3, default=(0.0, 0.0, 0.0))
|
|
scale: bpy.props.FloatVectorProperty(subtype="XYZ", size=3, default=(1.0, 1.0, 1.0))
|
|
disabled: bpy.props.BoolProperty(default=False)
|
|
|
|
def validate(self, report=None):
|
|
if not self.disabled and not utils.image_exists(self.image):
|
|
rep = f"Texture mapping: {self.texture_type} is no longer valid."
|
|
utils.log_info(rep)
|
|
if report is not None:
|
|
report.append(rep)
|
|
self.invalidate()
|
|
return not self.disabled
|
|
|
|
def invalidate(self):
|
|
utils.log_detail(f" - Invalidating Texture mapping: {self.texture_type}")
|
|
self.disabled = True
|
|
|
|
def delete(self):
|
|
if self.disabled and utils.image_exists(self.image):
|
|
utils.log_detail(f" - Deleting texture mapping image: {self.image.name}")
|
|
utils.purge_image(self.image)
|
|
|
|
def clean_up(self):
|
|
if self.disabled and utils.image_exists(self.image):
|
|
utils.log_detail(f" - Cleaning up texture mapping: {self.image.name}")
|
|
self.image = None
|
|
# endregion
|
|
|
|
# region MaterialCache
|
|
class CC3MaterialCache:
|
|
material_id: bpy.props.StringProperty(default="")
|
|
material: bpy.props.PointerProperty(type=bpy.types.Material)
|
|
source_name: bpy.props.StringProperty(default="")
|
|
material_type: bpy.props.EnumProperty(items=vars.ENUM_MATERIAL_TYPES, default="DEFAULT", update=lambda s,c: update_material_property(s,c,"material_type"))
|
|
texture_mappings: bpy.props.CollectionProperty(type=CC3TextureMapping)
|
|
#parameters: bpy.props.PointerProperty(type=CC3MaterialParameters)
|
|
mixer_settings: bpy.props.PointerProperty(type=channel_mixer.CC3MixerSettings)
|
|
dir: bpy.props.StringProperty(default="")
|
|
user_added: bpy.props.BoolProperty(default=False)
|
|
temp_weight_map: bpy.props.PointerProperty(type=bpy.types.Image)
|
|
alpha_is_diffuse: bpy.props.BoolProperty(default=False)
|
|
alpha_mode: bpy.props.StringProperty(default="NONE") # NONE, BLEND, HASHED, OPAQUE
|
|
culling_sides: bpy.props.IntProperty(default=0) # 0 - default, 1 - single sided, 2 - double sided
|
|
cloth_physics: bpy.props.StringProperty(default="DEFAULT") # DEFAULT, OFF, ON
|
|
render_target: bpy.props.EnumProperty(items=[
|
|
("NONE","None","Not Set."),
|
|
("EEVEE","Eevee","Build shaders for Eevee rendering."),
|
|
("CYCLES","Cycles","Build shaders for Cycles rendering."),
|
|
], default="NONE", name = "Target Renderer")
|
|
disabled: bpy.props.BoolProperty(default=False)
|
|
|
|
def set_texture_mapping(self, texture_type, texture_path, embedded, image, location, rotation, scale):
|
|
mapping = self.get_texture_mapping(texture_type)
|
|
if mapping is None:
|
|
mapping = self.texture_mappings.add()
|
|
mapping.texture_type = texture_type
|
|
mapping.texture_path = texture_path
|
|
mapping.embedded = embedded
|
|
mapping.image = image
|
|
mapping.location = location
|
|
mapping.rotation = rotation
|
|
mapping.scale = scale
|
|
|
|
def get_tex_dir(self, chr_cache):
|
|
if os.path.isabs(self.dir):
|
|
return os.path.normpath(self.dir)
|
|
else:
|
|
return os.path.normpath(os.path.join(chr_cache.get_import_dir(), self.dir))
|
|
|
|
def get_texture_mapping(self, texture_type):
|
|
for mapping in self.texture_mappings:
|
|
if mapping.texture_type == texture_type:
|
|
return mapping
|
|
return None
|
|
|
|
def is_sss(self):
|
|
return self.material_type == "SSS"
|
|
|
|
def is_skin(self):
|
|
return (self.material_type == "SKIN_HEAD"
|
|
or self.material_type == "SKIN_BODY"
|
|
or self.material_type == "SKIN_ARM"
|
|
or self.material_type == "SKIN_LEG")
|
|
|
|
def is_head(self):
|
|
return self.material_type == "SKIN_HEAD"
|
|
|
|
def is_body(self):
|
|
return self.material_type == "SKIN_BODY"
|
|
|
|
def is_arm(self):
|
|
return self.material_type == "SKIN_ARM"
|
|
|
|
def is_leg(self):
|
|
return self.material_type == "SKIN_LEG"
|
|
|
|
def is_teeth(self):
|
|
return (self.material_type == "TEETH_UPPER"
|
|
or self.material_type == "TEETH_LOWER")
|
|
|
|
def is_upper_teeth(self):
|
|
return self.material_type == "TEETH_UPPER"
|
|
|
|
def is_tongue(self):
|
|
return self.material_type == "TONGUE"
|
|
|
|
def is_hair(self):
|
|
return self.material_type == "HAIR"
|
|
|
|
def is_scalp(self):
|
|
return self.material_type == "SCALP"
|
|
|
|
def is_eyelash(self):
|
|
return self.material_type == "EYELASH"
|
|
|
|
def is_nails(self):
|
|
return self.material_type == "NAILS"
|
|
|
|
def is_eye(self, side = "ANY"):
|
|
if side == "RIGHT":
|
|
return self.material_type == "EYE_RIGHT"
|
|
elif side == "LEFT":
|
|
return self.material_type == "EYE_LEFT"
|
|
else:
|
|
return (self.material_type == "EYE_RIGHT"
|
|
or self.material_type == "EYE_LEFT")
|
|
|
|
def is_cornea(self, side = "ANY"):
|
|
if side == "RIGHT":
|
|
return self.material_type == "CORNEA_RIGHT"
|
|
elif side == "LEFT":
|
|
return self.material_type == "CORNEA_LEFT"
|
|
else:
|
|
return (self.material_type == "CORNEA_RIGHT"
|
|
or self.material_type == "CORNEA_LEFT")
|
|
|
|
def is_eye_occlusion(self):
|
|
return (self.material_type == "OCCLUSION_RIGHT" or
|
|
self.material_type == "OCCLUSION_LEFT" or
|
|
self.material_type == "OCCLUSION_PLUS_RIGHT" or
|
|
self.material_type == "OCCLUSION_PLUS_LEFT")
|
|
|
|
def is_tearline(self):
|
|
return (self.material_type == "TEARLINE_RIGHT" or
|
|
self.material_type == "TEARLINE_LEFT" or
|
|
self.material_type == "TEARLINE_PLUS_RIGHT" or
|
|
self.material_type == "TEARLINE_PLUS_LEFT")
|
|
|
|
def get_base_name(self):
|
|
return utils.strip_name(self.material.name)
|
|
|
|
def get_material_type(self):
|
|
return self.material_type
|
|
|
|
def get_material_id(self):
|
|
if not self.material_id:
|
|
self.material_id = utils.generate_random_id(20)
|
|
return self.material_id
|
|
|
|
def get_render_target(self):
|
|
# return render target if set
|
|
if self.render_target != "NONE":
|
|
return self.render_target
|
|
# fall back to scene render engine
|
|
return "CYCLES" if bpy.context.scene.render.engine == "CYCLES" else "EEVEE"
|
|
|
|
def check_id(self):
|
|
material_id = self.get_material_id()
|
|
material_type = self.material_type
|
|
if self.material:
|
|
self.material["rl_material_id"] = material_id
|
|
self.material["rl_material_type"] = material_type
|
|
|
|
def validate(self, report=None):
|
|
if not self.disabled and not utils.material_exists(self.material):
|
|
rep = f"Material: {self.source_name} no longer valid."
|
|
utils.log_info(rep)
|
|
if report is not None:
|
|
report.append(rep)
|
|
self.invalidate()
|
|
else:
|
|
tex_mapping: CC3TextureMapping
|
|
for tex_mapping in self.texture_mappings:
|
|
tex_mapping.validate(report)
|
|
self.mixer_settings.validate(report)
|
|
return not self.disabled
|
|
|
|
def invalidate(self):
|
|
utils.log_info(f"Invalidating Material cache: {self.source_name}")
|
|
self.disabled = True
|
|
tex_mapping: CC3TextureMapping
|
|
for tex_mapping in self.texture_mappings:
|
|
tex_mapping.invalidate()
|
|
self.mixer_settings.invalidate()
|
|
|
|
def delete(self):
|
|
tex_mapping: CC3TextureMapping
|
|
for tex_mapping in self.texture_mappings:
|
|
tex_mapping.delete()
|
|
self.mixer_settings.delete()
|
|
if self.disabled:
|
|
if utils.material_exists(self.material):
|
|
utils.log_detail(f"Deleting Material: {self.material.name}")
|
|
bpy.data.materials.remove(self.material)
|
|
if utils.image_exists(self.temp_weight_map):
|
|
utils.log_detail(f" - Deleting Temporary weight map image: {self.temp_weight_map.name}")
|
|
utils.purge_image(self.temp_weight_map)
|
|
|
|
def clean_up(self):
|
|
tex_mapping: CC3TextureMapping
|
|
for tex_mapping in self.texture_mappings:
|
|
tex_mapping.clean_up()
|
|
self.mixer_settings.clean_up()
|
|
if self.disabled:
|
|
utils.log_detail(f"Cleaning up material cache: {self.source_name}")
|
|
self.texture_mappings.clear()
|
|
self.material = None
|
|
self.temp_weight_map = None
|
|
|
|
|
|
class CC3EyeMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3EyeParameters)
|
|
|
|
class CC3EyeOcclusionMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3EyeOcclusionParameters)
|
|
|
|
class CC3TearlineMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3TearlineParameters)
|
|
|
|
class CC3TeethMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3TeethParameters)
|
|
|
|
class CC3TongueMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3TongueParameters)
|
|
|
|
class CC3HairMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3HairParameters)
|
|
|
|
class CC3HeadMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3HeadParameters)
|
|
|
|
class CC3SkinMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3SkinParameters)
|
|
|
|
class CC3PBRMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3PBRParameters)
|
|
|
|
class CC3SSSMaterialCache(bpy.types.PropertyGroup, CC3MaterialCache):
|
|
parameters: bpy.props.PointerProperty(type=CC3SSSParameters)
|
|
# endregion
|
|
|
|
# region ObjectCache
|
|
class CC3ObjectCache(bpy.types.PropertyGroup):
|
|
object_id: bpy.props.StringProperty(default="")
|
|
object: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
source_name: bpy.props.StringProperty(default="")
|
|
object_type: bpy.props.EnumProperty(items=vars.ENUM_OBJECT_TYPES, default="DEFAULT", update=lambda s,c: update_object_property(s,c,"object_type"))
|
|
collision_physics: bpy.props.StringProperty(default="DEFAULT") # DEFAULT, OFF, ON, PROXY
|
|
cloth_physics: bpy.props.StringProperty(default="DEFAULT") # DEFAULT, OFF, ON
|
|
cloth_settings: bpy.props.StringProperty(default="DEFAULT") # DEFAULT, HAIR, COTTON, DENIM, LEATHER, RUBBER, SILK
|
|
cloth_self_collision: bpy.props.BoolProperty(default=False)
|
|
user_added: bpy.props.BoolProperty(default=False)
|
|
use_collision_proxy: bpy.props.BoolProperty(default=False)
|
|
collision_proxy_decimate: bpy.props.FloatProperty(default=0.125, min=0.0, max=1.0)
|
|
vertex_count: bpy.props.IntProperty(default=0)
|
|
face_count: bpy.props.IntProperty(default=0)
|
|
edge_count: bpy.props.IntProperty(default=0)
|
|
disabled: bpy.props.BoolProperty(default=False)
|
|
json_path: bpy.props.StringProperty(default="")
|
|
|
|
def is_body(self):
|
|
return self.object_type == "BODY"
|
|
|
|
def is_teeth(self):
|
|
return self.object_type == "TEETH"
|
|
|
|
def is_tongue(self):
|
|
return self.object_type == "TONGUE"
|
|
|
|
def is_hair(self):
|
|
return self.object_type == "HAIR"
|
|
|
|
def is_eye(self):
|
|
return self.object_type == "EYE"
|
|
|
|
def is_eye_occlusion(self):
|
|
return (self.object_type == "OCCLUSION" or self.object_type == "OCCLUSION_PLUS")
|
|
|
|
def is_tearline(self):
|
|
return (self.object_type == "TEARLINE" or self.object_type == "TEARLINE_PLUS")
|
|
|
|
def is_mesh(self):
|
|
return utils.object_exists_is_mesh(self.object)
|
|
|
|
def is_armature(self):
|
|
return utils.object_exists_is_armature(self.object)
|
|
|
|
def get_object(self, return_invalid=False):
|
|
if utils.object_exists(self.object):
|
|
return self.object
|
|
if return_invalid:
|
|
return self.object
|
|
return None
|
|
|
|
def get_base_name(self):
|
|
return utils.strip_name(self.object.name)
|
|
|
|
def get_mesh(self):
|
|
if utils.object_exists_is_mesh(self.object):
|
|
return self.object
|
|
return None
|
|
|
|
def set_object(self, obj):
|
|
if obj and utils.object_exists(obj):
|
|
self.object = obj
|
|
elif obj is None:
|
|
self.object = None
|
|
|
|
def set_object_type(self, type):
|
|
if type is not None:
|
|
self.object_type = type
|
|
self.object["rl_object_type"] = type
|
|
if type == "BODY":
|
|
self.use_collision_proxy = True
|
|
|
|
def check_id(self):
|
|
if self.object_id == "":
|
|
self.object_id = utils.generate_random_id(20)
|
|
if self.object:
|
|
utils.set_rl_object_id(self.object, self.object_id)
|
|
self.object["rl_object_type"] = self.object_type
|
|
|
|
def validate(self, report=None, split_objects=None):
|
|
objects_exist = utils.object_exists(self.object)
|
|
if not objects_exist and split_objects:
|
|
for split_obj in split_objects:
|
|
if utils.object_exists(split_obj):
|
|
objects_exist = True
|
|
break
|
|
if not self.disabled and not objects_exist:
|
|
rep = f"Object: {self.source_name} no longer valid."
|
|
utils.log_info(rep)
|
|
if report is not None:
|
|
report.append(rep)
|
|
self.invalidate()
|
|
return not self.disabled
|
|
|
|
def invalidate(self):
|
|
utils.log_info(f"Invalidating Object cache: {self.source_name}")
|
|
self.disabled = True
|
|
|
|
def delete(self):
|
|
if self.disabled:
|
|
if utils.object_exists(self.object):
|
|
utils.log_info(f"Deleting Object: {self.object}")
|
|
utils.delete_object_tree(self.object)
|
|
|
|
def clean_up(self):
|
|
if self.disabled:
|
|
utils.log_info(f"Cleaning up Object: {self.source_name}")
|
|
pass
|
|
|
|
def validate_topography(self):
|
|
obj = self.get_object()
|
|
if utils.object_exists_is_mesh(obj):
|
|
return (self.vertex_count == len(obj.data.vertices) and
|
|
self.face_count == len(obj.data.polygons) and
|
|
self.edge_count == len(obj.data.edges))
|
|
# endregion
|
|
|
|
#region ExpressionData
|
|
class CCICExpressionData(bpy.types.PropertyGroup):
|
|
key_name: bpy.props.StringProperty()
|
|
bone_name: bpy.props.StringProperty()
|
|
translation: bpy.props.FloatVectorProperty()
|
|
rotation: bpy.props.FloatVectorProperty()
|
|
rigify_bone_name: bpy.props.StringProperty()
|
|
rigify_translation: bpy.props.FloatVectorProperty()
|
|
rigify_rotation: bpy.props.FloatVectorProperty()
|
|
offset_bone_name: bpy.props.StringProperty()
|
|
offset_translation: bpy.props.FloatVectorProperty()
|
|
offset_rotation: bpy.props.FloatVectorProperty()
|
|
# endregion
|
|
|
|
# region CharacterCache
|
|
class CC3CharacterCache(bpy.types.PropertyGroup):
|
|
open_mouth: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=open_mouth_update)
|
|
eye_close: bpy.props.FloatProperty(default=0.0, min=0, max=1, update=eye_close_update)
|
|
import_file: bpy.props.StringProperty(default="", subtype="FILE_PATH")
|
|
#
|
|
tongue_material_cache: bpy.props.CollectionProperty(type=CC3TongueMaterialCache)
|
|
teeth_material_cache: bpy.props.CollectionProperty(type=CC3TeethMaterialCache)
|
|
head_material_cache: bpy.props.CollectionProperty(type=CC3HeadMaterialCache)
|
|
skin_material_cache: bpy.props.CollectionProperty(type=CC3SkinMaterialCache)
|
|
tearline_material_cache: bpy.props.CollectionProperty(type=CC3TearlineMaterialCache)
|
|
eye_occlusion_material_cache: bpy.props.CollectionProperty(type=CC3EyeOcclusionMaterialCache)
|
|
eye_material_cache: bpy.props.CollectionProperty(type=CC3EyeMaterialCache)
|
|
hair_material_cache: bpy.props.CollectionProperty(type=CC3HairMaterialCache)
|
|
pbr_material_cache: bpy.props.CollectionProperty(type=CC3PBRMaterialCache)
|
|
sss_material_cache: bpy.props.CollectionProperty(type=CC3SSSMaterialCache)
|
|
#
|
|
basic_parameters: bpy.props.PointerProperty(type=CC3BasicParameters)
|
|
#
|
|
object_cache: bpy.props.CollectionProperty(type=CC3ObjectCache)
|
|
expression_set: bpy.props.CollectionProperty(type=CCICExpressionData)
|
|
# import file name without extension
|
|
import_flags: bpy.props.IntProperty(default=0)
|
|
import_embedded: bpy.props.BoolProperty(default=False)
|
|
# which character in the import
|
|
link_id: bpy.props.StringProperty(default="")
|
|
character_name: bpy.props.StringProperty(default="")
|
|
generation: bpy.props.StringProperty(default="None")
|
|
parent_object: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
# accessory parent bone selector
|
|
accessory_parent_bone: bpy.props.StringProperty(default="CC_Base_Head")
|
|
# counter (how many times have the materials been built)
|
|
build_count: bpy.props.IntProperty(default=0)
|
|
# auto index
|
|
auto_index: bpy.props.IntProperty(default=0)
|
|
# facial profile
|
|
facial_profile: bpy.props.EnumProperty(items=[
|
|
("NONE","None","None"),
|
|
("UNKNOWN","Unknown","Unknown"),
|
|
("TRA","Traditional",""),
|
|
("STD","Standard",""),
|
|
("EXT","Extended",""),
|
|
("MH","CC5 HD","")
|
|
], default="NONE")
|
|
viseme_profile: bpy.props.EnumProperty(items=[
|
|
("NONE","None","None"),
|
|
("UNKNOWN","Unknown","Unknown"),
|
|
("DIRECT","Direct",""),
|
|
("PAIRS3","Pairs (CC3)",""),
|
|
("PAIRS4","Pairs (CC4)",""),
|
|
], default="NONE")
|
|
|
|
setup_mode: bpy.props.EnumProperty(items=[
|
|
("BASIC","Basic","Build basic PBR materials."),
|
|
("ADVANCED","Advanced","Build advanced materials with blend maps, subsurface, and micro normals, specular and roughness control and includes layered eye, teeth and tongue materials.")
|
|
], default="ADVANCED")
|
|
|
|
render_target: bpy.props.EnumProperty(items=[
|
|
("NONE","None","Not Set."),
|
|
("EEVEE","Eevee","Build shaders for Eevee rendering."),
|
|
("CYCLES","Cycles","Build shaders for Cycles rendering."),
|
|
], default="NONE", name = "Target Renderer")
|
|
|
|
physics_disabled: bpy.props.BoolProperty(default=False)
|
|
physics_applied: bpy.props.BoolProperty(default=False)
|
|
|
|
rigified: bpy.props.BoolProperty(default=False)
|
|
rigified_full_face_rig: bpy.props.BoolProperty(default=False)
|
|
rigify_expression_rig: bpy.props.EnumProperty(items=[
|
|
("NONE","None","No expression rig, just eye and jaw controls"),
|
|
("RIGIFY","Rigify","Rigify full face rig"),
|
|
("META","CC5 HD","HD Face Control expression rig"),
|
|
], default="META", name="Expression Rig")
|
|
rig_mode: bpy.props.EnumProperty(items=[
|
|
("QUICK","Quick","Rig the character all in one go."),
|
|
("ADVANCED","Advanced","Split the process so that user adjustments can be made to the meta rig before generating."),
|
|
], default="QUICK", name = "Rigging Mode")
|
|
rig_meta_rig: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
rig_export_rig: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
rig_original_rig: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
rig_retarget_rig: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
rig_datalink_rig: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
rig_retarget_source_rig: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
retarget_heel_correction_angle: bpy.props.FloatProperty(default = 0.0, min=-0.7854, max=0.7854, description="Heel pitch angle (radians)")
|
|
retarget_arm_correction_angle: bpy.props.FloatProperty(default = 0.0, min=-0.2618, max=0.2618, description="Arm spread angle (radians)")
|
|
retarget_leg_correction_angle: bpy.props.FloatProperty(default = 0.0, min=-0.2618, max=0.2618, description="Leg spread angle (radians)")
|
|
retarget_z_correction_height: bpy.props.FloatProperty(default = 0.0, min=-0.2, max=0.2, description="Height Adjustment (m)")
|
|
arkit_proxy: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
|
|
non_standard_type: bpy.props.EnumProperty(items=[
|
|
("HUMANOID","Humanoid","Non standard character is a Humanoid"),
|
|
("CREATURE","Creature","Non standard character is a Creature"),
|
|
("PROP","Prop","Non standard character is a Prop"),
|
|
], default="HUMANOID", name = "Non-standard Character Type")
|
|
|
|
detail_sculpt_sub_target: bpy.props.EnumProperty(items=[
|
|
("HEAD","Head","Sculpt on the head only"),
|
|
("BODY","Body","Sculpt on the body only"),
|
|
("ALL","All","Sculpt the entire body"),
|
|
], default="HEAD", name = "Sculpt Target")
|
|
|
|
detail_multires_body: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
sculpt_multires_body: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
|
|
detail_normal_strength: bpy.props.FloatProperty(default=1.0, min = -10.0, max = 10.0,
|
|
description="Strength of the detail sculpt normal overlay.",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"detail_normal_strength"))
|
|
detail_ao_strength: bpy.props.FloatProperty(default=0.5, min = 0.0, max = 2.0,
|
|
description="Strength of the detail sculpt ambient occlusion overlay.",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"detail_ao_strength"))
|
|
detail_normal_definition: bpy.props.FloatProperty(default=10, min = 0, max = 40.0,
|
|
description="Mask definition of the detail sculpt normal overlay.\n"
|
|
"Lower definition shrinks the mask around the sculpted areas and smooths the transition between normal layers.",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"detail_normal_definition"))
|
|
body_normal_strength: bpy.props.FloatProperty(default=1.0, min = -10.0, max = 10.0,
|
|
description="Strength of the body sculpt normal overlay.",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"body_normal_strength"))
|
|
body_ao_strength: bpy.props.FloatProperty(default=0.5, min = 0.0, max = 2.0,
|
|
description="Strength of the body sculpt ambient occlusion overlay.",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"body_ao_strength"))
|
|
body_normal_definition: bpy.props.FloatProperty(default=10, min = 0, max = 40.0,
|
|
description="Mask definition of the body sculpt normal overlay.\n"
|
|
"Lower definition shrinks the mask around the sculpted areas and smooths the transition between normal layers.",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"body_normal_definition"))
|
|
|
|
multires_bake_apply: bpy.props.BoolProperty(default=True, name="Apply Multi-res Base Shape",
|
|
description="Copy the multi-res base shape back to original character when baking the body sculpt normals.\n"
|
|
"Only the vertices affected by the sculpt are copied back and this does not destroy the original character's shapekeys.")
|
|
|
|
detail_mix_mode: bpy.props.EnumProperty(items=[
|
|
("OVERLAY","Overlay","Sculpted normals and occlusion are overlayed on top of the base normals and occlusion."),
|
|
("REPLACE","Replace","Sculpted normals and occlusion replaces the base normals and occlusion."),
|
|
], default="OVERLAY", name = "Detail Mix Mode",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"detail_mix_mode"))
|
|
|
|
body_mix_mode: bpy.props.EnumProperty(items=[
|
|
("OVERLAY","Overlay","Sculpted normals and occlusion are overlayed on top of the base normals and occlusion."),
|
|
("REPLACE","Replace","Sculpted normals and occlusion replaces the base normals and occlusion."),
|
|
], default="OVERLAY", name = "Body Mix Mode",
|
|
update=lambda s,c: update_sculpt_mix_node(s,c,"body_mix_mode"))
|
|
|
|
available_spring_rigs: bpy.props.EnumProperty(items=springbones.enumerate_spring_rigs,
|
|
default=0,
|
|
name="Available Spring Rigs",
|
|
description="A list of all the spring rigs on the character")
|
|
|
|
proportion_editing: bpy.props.BoolProperty(default=False)
|
|
proportion_editing_in_front: bpy.props.BoolProperty(default=False)
|
|
proportion_editing_actions: bpy.props.CollectionProperty(type=CCICActionStore)
|
|
proportion_editing_scale: bpy.props.EnumProperty(items=[
|
|
("FULL","Full","Full"),
|
|
("FIX_SHEAR","Fix Shear","Fix Shear"),
|
|
("ALIGNED","Aligned","Aligned"),
|
|
("AVERAGE","Average","Average"),
|
|
("NONE","None","None"),
|
|
("NONE_LEGACY","None (Legacy)","None (Legacy)"),
|
|
], default="FULL", name="Set bone inherit scale")
|
|
|
|
baked_target_mode: bpy.props.EnumProperty(items=vars.BAKE_TARGETS, default="NONE")
|
|
|
|
rigify_face_control_color: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 0.88, 0.11, 1.0),
|
|
min = 0.0, max = 1.0,
|
|
name="Rig Color",
|
|
update=update_facerig_color)
|
|
|
|
action_options: bpy.props.PointerProperty(type=CCICActionOptions)
|
|
|
|
disabled: bpy.props.BoolProperty(default=False)
|
|
|
|
def set_link_id(self, link_id):
|
|
self.link_id = link_id
|
|
arm = self.get_armature()
|
|
utils.set_rl_link_id(arm, link_id)
|
|
|
|
def get_auto_index(self):
|
|
self.auto_index += 1
|
|
return self.auto_index
|
|
|
|
def select(self, only=True):
|
|
arm = self.get_armature()
|
|
if arm:
|
|
utils.try_select_object(arm, clear_selection=only)
|
|
utils.set_active_object(arm)
|
|
else:
|
|
self.select_all()
|
|
|
|
def select_all(self, only=True):
|
|
objects = self.get_all_objects(include_armature=True,
|
|
include_children=True)
|
|
utils.try_select_objects(objects, clear_selection=only)
|
|
arm = self.get_armature()
|
|
if arm:
|
|
utils.set_active_object(arm)
|
|
|
|
def get_tex_dir(self):
|
|
dir, file = os.path.split(self.import_file)
|
|
name, ext = os.path.splitext(file)
|
|
if ext.lower() == ".fbx":
|
|
tex_dir = os.path.join(dir, name + ".fbm")
|
|
else:
|
|
tex_dir = os.path.join(dir, name)
|
|
return os.path.normpath(tex_dir)
|
|
|
|
def get_import_dir(self):
|
|
dir, file = os.path.split(self.import_file)
|
|
return dir
|
|
|
|
def get_import_key_file(self):
|
|
dir, file = os.path.split(self.import_file)
|
|
name, ext = os.path.splitext(file)
|
|
if ext.lower() == ".fbx":
|
|
key_file = os.path.join(dir, name + ".fbxkey")
|
|
else:
|
|
key_file = os.path.join(dir, name + ".ObjKey")
|
|
return key_file
|
|
|
|
def get_import_has_key(self):
|
|
key_file = self.get_import_key_file()
|
|
return os.path.exists(key_file)
|
|
|
|
def get_import_type(self):
|
|
dir, file = os.path.split(self.import_file)
|
|
name, ext = os.path.splitext(file)
|
|
return ext[1:]
|
|
|
|
def is_import_type(self, import_type: str):
|
|
if import_type[0] == ".":
|
|
import_type = import_type[1:]
|
|
return self.get_import_type().lower() == import_type.lower()
|
|
|
|
def get_character_id(self):
|
|
dir, file = os.path.split(self.import_file)
|
|
name, ext = os.path.splitext(file)
|
|
return name
|
|
|
|
def check_paths(self):
|
|
local_dir = utils.local_path()
|
|
if local_dir and self.import_file and not os.path.exists(self.import_file):
|
|
utils.log_info(f"Import source file no longer exists: {self.import_file}")
|
|
dir, name = os.path.split(self.import_file)
|
|
local_file = os.path.join(local_dir, name)
|
|
utils.log_info(f"Looking for moved source file: {local_file}")
|
|
if os.path.exists(local_file):
|
|
utils.log_info(f"Updating paths to source file: {local_file}")
|
|
self.import_file = local_file
|
|
|
|
def can_standard_export(self):
|
|
prefs = vars.prefs()
|
|
result = True
|
|
if prefs.export_require_key:
|
|
if self.generation in vars.STANDARD_GENERATIONS and not self.get_import_has_key():
|
|
result = False
|
|
if self.rigified:
|
|
result = False
|
|
return result
|
|
|
|
def can_go_cc(self):
|
|
if self.rigified:
|
|
return False
|
|
if self.is_standard() and self.get_import_has_key():
|
|
return True
|
|
if self.is_morph():
|
|
return True
|
|
if self.is_non_standard():
|
|
return True
|
|
if self.is_prop():
|
|
return True
|
|
return False
|
|
|
|
def can_go_ic(self):
|
|
if self.is_prop():
|
|
return True
|
|
return False
|
|
|
|
def is_morph(self):
|
|
return self.is_import_type("OBJ") and self.get_import_has_key()
|
|
|
|
def is_standard(self):
|
|
return self.generation in vars.STANDARD_GENERATIONS
|
|
|
|
def is_non_standard(self, include_props=True):
|
|
if include_props:
|
|
return self.generation not in vars.STANDARD_GENERATIONS
|
|
else:
|
|
return self.generation not in vars.STANDARD_GENERATIONS and self.generation not in vars.PROP_GENERATIONS
|
|
|
|
def is_avatar(self):
|
|
return not self.is_prop()
|
|
|
|
def is_prop(self):
|
|
if self.generation in vars.PROP_GENERATIONS:
|
|
return True
|
|
if self.non_standard_type == "PROP":
|
|
return True
|
|
return False
|
|
|
|
def cache_type(self):
|
|
if self.is_avatar():
|
|
return "AVATAR"
|
|
else:
|
|
return "PROP"
|
|
|
|
def get_render_target(self):
|
|
# return render target if set
|
|
if self.render_target != "NONE":
|
|
return self.render_target
|
|
# fall back to scene render engine
|
|
return "CYCLES" if bpy.context.scene.render.engine == "CYCLES" else "EEVEE"
|
|
|
|
def is_actor_core(self):
|
|
if (self.generation == "ActorCore"
|
|
or self.generation == "ActorScan"):
|
|
return True
|
|
return False
|
|
|
|
def can_hair_spring_rig(self):
|
|
"""Returns True if the character can have a hair spring rig."""
|
|
if (self.generation == "G3" or
|
|
self.generation == "G3Plus" or
|
|
self.generation == "NonStandardG3" or
|
|
self.generation == "ActorBuild" or
|
|
self.generation == "AccuRig"):
|
|
return True
|
|
elif self.generation == "GameBase":
|
|
return True
|
|
return False
|
|
|
|
def can_be_rigged(self):
|
|
"""Returns True if the character can be rigified."""
|
|
if self.rigified:
|
|
return False
|
|
if (self.generation == "G3" or
|
|
self.generation == "G3Plus" or
|
|
self.generation == "NonStandardG3" or
|
|
self.generation == "ActorBuild" or
|
|
self.generation == "AccuRig"):
|
|
return True
|
|
elif self.is_actor_core():
|
|
return True
|
|
elif self.generation == "GameBase":
|
|
return True
|
|
return False
|
|
|
|
def can_expression_rig(self):
|
|
if (self.generation == "G3" or
|
|
self.generation == "G3Plus" or
|
|
self.generation == "ActorBuild" or
|
|
self.generation == "ActorCore" or
|
|
self.generation == "ActorScan" or
|
|
self.generation == "GameBase"):
|
|
facial_profile, viseme_profile = self.get_facial_profile()
|
|
if facial_profile in ["STD", "EXT", "TRA", "MH"]:
|
|
return True
|
|
return False
|
|
|
|
def can_rigify_face(self):
|
|
if (self.generation == "G3" or
|
|
self.generation == "G3Plus"):
|
|
return True
|
|
return False
|
|
|
|
def allow_rigify(self):
|
|
prefs = vars.prefs()
|
|
if self.rigified:
|
|
return False
|
|
can_expresion_rig = self.can_expression_rig()
|
|
can_rigify_face = self.can_rigify_face()
|
|
return ((prefs.rigify_expression_rig == "META" and can_expresion_rig) or
|
|
(prefs.rigify_expression_rig == "RIGIFY" and can_rigify_face) or
|
|
(prefs.rigify_expression_rig == "NONE"))
|
|
|
|
def get_facial_profile(self, update=True):
|
|
if self.facial_profile != "NONE" and self.viseme_profile != "NONE":
|
|
return self.facial_profile, self.viseme_profile
|
|
else:
|
|
objects = self.get_cache_objects()
|
|
facial_profile, viseme_profile = meshutils.get_facial_profile(objects)
|
|
if update:
|
|
meshutils.set_facial_profile(objects, facial_profile, viseme_profile)
|
|
return facial_profile, viseme_profile
|
|
|
|
def get_facial_profile_names(self, update=True):
|
|
facial_profile, viseme_profile = self.get_facial_profile(update)
|
|
return utils.get_enum_prop_name(self, "facial_profile", facial_profile), \
|
|
utils.get_enum_prop_name(self, "viseme_profile", viseme_profile),
|
|
|
|
def get_rig_mapping_data(self):
|
|
return rigify_mapping_data.get_mapping_for_generation(self.generation)
|
|
|
|
def get_rig_bone_mapping(self):
|
|
rigify_data = rigify_mapping_data.get_mapping_for_generation(self.generation)
|
|
if rigify_data:
|
|
return rigify_data.bone_mapping
|
|
return None
|
|
|
|
def get_all_materials_cache(self, include_disabled=False):
|
|
cache_all = []
|
|
for mat_cache in self.tongue_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.teeth_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.head_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.skin_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.tearline_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.eye_occlusion_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.eye_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.hair_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.pbr_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
for mat_cache in self.sss_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
cache_all.append(mat_cache)
|
|
return cache_all
|
|
|
|
def get_all_materials(self, include_disabled=False):
|
|
materials = []
|
|
for mat_cache in self.tongue_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.teeth_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.head_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.skin_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.tearline_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.eye_occlusion_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.eye_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.hair_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.pbr_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
for mat_cache in self.sss_material_cache:
|
|
if mat_cache.material and (include_disabled or not mat_cache.disabled):
|
|
materials.append(mat_cache.material)
|
|
return materials
|
|
|
|
def is_sculpt_object(self, obj):
|
|
return ("rl_multires_BODY" in obj or "rl_multires_DETAIL" in obj or
|
|
obj == self.detail_multires_body or obj == self.sculpt_multires_body)
|
|
|
|
def is_collision_object(self, obj):
|
|
source, proxy, is_proxy = self.get_related_physics_objects(obj)
|
|
result = ("rl_collision_proxy" in obj or obj.name.endswith("_Collision_Proxy") or is_proxy)
|
|
return result
|
|
|
|
def is_rigidbody_collider(self, obj):
|
|
collider_collection = rigidbody.get_rigidbody_collider_collection()
|
|
if collider_collection and obj.name in collider_collection.objects:
|
|
return True
|
|
return False
|
|
|
|
def is_related_object(self, obj):
|
|
"""Objects not directly part of the character, but related to or used by the character in Blender.
|
|
If selected should indicate this chr_cache as the selected character.
|
|
These objects should not be exported."""
|
|
if self.is_sculpt_object(obj):
|
|
return True
|
|
elif self.is_collision_object(obj):
|
|
return True
|
|
elif self.rig_meta_rig and self.rig_meta_rig == obj:
|
|
return True
|
|
elif self.arkit_proxy and (self.arkit_proxy == obj or self.arkit_proxy == obj.parent):
|
|
return True
|
|
elif self.rigified and self.rig_original_rig and self.rig_original_rig == obj:
|
|
return True
|
|
elif self.is_rigidbody_collider(obj):
|
|
return True
|
|
return False
|
|
|
|
def get_all_objects(self,
|
|
include_armature=True,
|
|
include_cache=True,
|
|
include_children=False,
|
|
include_disabled=False,
|
|
include_split=True,
|
|
include_proxy=False,
|
|
include_sculpt=False,
|
|
of_type="ALL",
|
|
only_selected=False):
|
|
|
|
objects = []
|
|
|
|
# cached objects
|
|
for obj_cache in self.object_cache:
|
|
if obj_cache.disabled and not include_disabled: continue
|
|
obj = obj_cache.get_object()
|
|
if obj and obj not in objects:
|
|
if obj.type == "ARMATURE":
|
|
include = include_armature
|
|
else:
|
|
include = include_cache
|
|
if only_selected and obj not in bpy.context.selected_objects:
|
|
include = False
|
|
if include:
|
|
if of_type == "ALL" or of_type == obj.type:
|
|
objects.append(obj)
|
|
|
|
# non cached objects
|
|
arm = self.get_armature()
|
|
if arm:
|
|
for child in arm.children:
|
|
if child not in objects and utils.object_exists(child):
|
|
include = include_children
|
|
if include_proxy and self.is_collision_object(child):
|
|
include = True
|
|
if include_sculpt and self.is_sculpt_object(child):
|
|
include = True
|
|
if include_split and self.is_split_object(child):
|
|
include = True
|
|
if only_selected and child not in bpy.context.selected_objects:
|
|
include = False
|
|
if include:
|
|
if of_type == "ALL" or child.type == of_type:
|
|
objects.append(child)
|
|
|
|
if include_sculpt:
|
|
if self.sculpt_multires_body and self.sculpt_multires_body not in objects:
|
|
objects.append(self.sculpt_multires_body)
|
|
if self.detail_multires_body and self.detail_multires_body not in objects:
|
|
objects.append(self.detail_multires_body)
|
|
|
|
return objects
|
|
|
|
def get_cache_objects(self):
|
|
return self.get_all_objects(include_armature=True,
|
|
include_cache=True,
|
|
include_children=False,
|
|
include_disabled=False,
|
|
include_split=True,
|
|
include_proxy=False,
|
|
include_sculpt=False,
|
|
of_type="ALL",
|
|
only_selected=False)
|
|
|
|
def get_collision_proxy(self, obj):
|
|
obj_cache = self.get_object_cache(obj)
|
|
arm = self.get_armature()
|
|
for child in arm.children:
|
|
if obj_cache.object_id == utils.get_rl_object_id(child):
|
|
if "rl_collision_proxy" in child and child["rl_collision_proxy"] == obj.name:
|
|
return child
|
|
proxy_name = obj.name + ".Collision_Proxy"
|
|
for child in arm.children:
|
|
if child.name == proxy_name:
|
|
return child
|
|
return None
|
|
|
|
def remove_mat_cache(self, mat):
|
|
"""Removes the material cache containing this material from the relevant material cache.
|
|
|
|
Note this will invalidate all current material cache references of the same type!
|
|
"""
|
|
if mat:
|
|
for mat_cache in self.tongue_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.tongue_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.teeth_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.teeth_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.head_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.head_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.skin_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.skin_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.tearline_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.tearline_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.eye_occlusion_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.eye_occlusion_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.eye_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.eye_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.hair_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.hair_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.pbr_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.pbr_material_cache, mat_cache)
|
|
return
|
|
for mat_cache in self.sss_material_cache:
|
|
if mat_cache.material == mat:
|
|
utils.remove_from_collection(self.sss_material_cache, mat_cache)
|
|
return
|
|
|
|
def is_split_object(self, obj) -> bool:
|
|
obj_cache = self.get_object_cache(obj, strict=False)
|
|
if obj_cache:
|
|
return obj_cache.get_object() != obj
|
|
return False
|
|
|
|
def get_object_cache(self, obj, include_disabled=False, by_id=None, strict=False) -> CC3ObjectCache:
|
|
"""Returns the object cache for this object.
|
|
"""
|
|
if utils.object_exists(obj):
|
|
# by object
|
|
if not strict and not by_id:
|
|
by_id = utils.get_rl_object_id(obj)
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
cache_object = obj_cache.get_object()
|
|
if cache_object and cache_object == obj:
|
|
return obj_cache
|
|
# by id
|
|
if by_id:
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
if obj_cache.object_id == by_id:
|
|
return obj_cache
|
|
return None
|
|
|
|
def remove_object_cache(self, obj):
|
|
"""Removes the object from the object cache.
|
|
|
|
Note this will invalidate all current object cache references!
|
|
"""
|
|
if obj:
|
|
for obj_cache in self.object_cache:
|
|
cache_object = obj_cache.get_object()
|
|
if cache_object and cache_object == obj:
|
|
utils.remove_from_collection(self.object_cache, obj_cache)
|
|
return
|
|
|
|
def has_cache_objects(self, objects, include_disabled=False):
|
|
"""Returns True if *any* of the objects are actively the object cache.
|
|
"""
|
|
object_ids = [ utils.get_rl_object_id(o) for o in objects ]
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
cache_object = obj_cache.get_object()
|
|
if cache_object in objects:
|
|
return True
|
|
if obj_cache.object_id and obj_cache.object_id in object_ids:
|
|
return True
|
|
return False
|
|
|
|
def has_child_objects(self, objects):
|
|
arm = self.get_armature()
|
|
if arm:
|
|
for obj in objects:
|
|
if obj.parent == arm:
|
|
return True
|
|
return False
|
|
|
|
def has_object(self, obj, include_disabled=False):
|
|
"""Returns True if the object is in the object cache.
|
|
"""
|
|
if obj:
|
|
object_id = utils.get_rl_object_id(obj)
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
cache_object = obj_cache.get_object()
|
|
cache_object_id = utils.get_rl_object_id(cache_object)
|
|
if cache_object == obj or cache_object_id == object_id:
|
|
return True
|
|
return False
|
|
|
|
def get_split_objects(self, obj):
|
|
split_objects = []
|
|
if type(obj) is CC3ObjectCache:
|
|
obj = obj.get_object()
|
|
if obj:
|
|
split_objects.append(obj)
|
|
obj_id = utils.get_rl_object_id(obj)
|
|
arm = self.get_armature()
|
|
if arm:
|
|
for child in arm.children:
|
|
if child not in split_objects:
|
|
child_id = utils.get_rl_object_id(child)
|
|
if child_id == obj_id:
|
|
split_objects.append(obj)
|
|
return split_objects
|
|
|
|
def get_armature(self, include_disabled=False):
|
|
try:
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
cache_object = obj_cache.get_object()
|
|
if utils.object_exists_is_armature(cache_object):
|
|
return cache_object
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
def get_body(self):
|
|
return self.get_object_of_type("BODY")
|
|
|
|
def get_body_cache(self):
|
|
return self.get_cache_of_type("BODY")
|
|
|
|
def get_objects_of_type(self, object_type):
|
|
objects = []
|
|
for obj_cache in self.object_cache:
|
|
if obj_cache.object_type == object_type:
|
|
body_id = obj_cache.object_id
|
|
chr_objects = self.get_cache_objects()
|
|
for obj in chr_objects:
|
|
if utils.get_rl_object_id(obj) == body_id:
|
|
objects.append(obj)
|
|
return objects
|
|
|
|
def get_object_of_type(self, object_type, include_disabled=False):
|
|
try:
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
cache_object = obj_cache.get_object()
|
|
if cache_object and obj_cache.object_type == object_type:
|
|
return cache_object
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
def get_cache_of_type(self, object_type, include_disabled=False):
|
|
try:
|
|
for obj_cache in self.object_cache:
|
|
if include_disabled or not obj_cache.disabled:
|
|
if obj_cache.object_type == object_type:
|
|
return obj_cache
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
def set_rigify_armature(self, new_arm):
|
|
self.rigified = True
|
|
try:
|
|
for obj_cache in self.object_cache:
|
|
if not obj_cache.disabled:
|
|
cache_object = obj_cache.get_object()
|
|
if cache_object.type == "ARMATURE":
|
|
self.rig_original_rig = cache_object
|
|
obj_cache.set_object(new_arm)
|
|
# update the object id
|
|
obj_cache.object_id = utils.generate_random_id(20)
|
|
utils.set_rl_object_id(new_arm, obj_cache.object_id)
|
|
utils.set_rl_link_id(new_arm, self.link_id)
|
|
except:
|
|
pass
|
|
|
|
def add_object_cache(self, obj, copy_from=None, user=False):
|
|
"""Returns the object cache for this object.
|
|
|
|
Fetches or creates an object cache for the object. Always returns an object cache collection.
|
|
"""
|
|
|
|
obj_cache: CC3ObjectCache = self.get_object_cache(obj)
|
|
if obj_cache is None:
|
|
utils.log_info(f"Creating Object Cache for: {obj.name}")
|
|
obj_cache = self.object_cache.add()
|
|
obj_cache.object_id = utils.generate_random_id(20)
|
|
if copy_from:
|
|
utils.log_info(f"Copying object cache from: {copy_from}")
|
|
utils.copy_property_group(copy_from, obj_cache)
|
|
if user:
|
|
obj_cache.user_added = True
|
|
obj_cache.object_id = utils.generate_random_id(20)
|
|
obj_cache.set_object(obj)
|
|
obj_cache.source_name = utils.strip_name(obj.name)
|
|
obj_cache.check_id()
|
|
if obj.type == "MESH":
|
|
obj_cache.vertex_count = len(obj.data.vertices)
|
|
obj_cache.face_count = len(obj.data.polygons)
|
|
obj_cache.edge_count = len(obj.data.edges)
|
|
return obj_cache
|
|
|
|
def has_material(self, mat):
|
|
return (self.get_material_cache(mat) is not None)
|
|
|
|
def has_any_materials(self, materials):
|
|
for mat in materials:
|
|
if mat and self.has_material(mat):
|
|
return True
|
|
return False
|
|
|
|
def has_all_materials(self, materials):
|
|
for mat in materials:
|
|
if mat and not self.has_material(mat):
|
|
return False
|
|
return True
|
|
|
|
def count_material(self, mat):
|
|
count = 0
|
|
for obj_cache in self.object_cache:
|
|
objects = self.get_split_objects(obj_cache)
|
|
for obj in objects:
|
|
if obj and obj.type == "MESH":
|
|
for m in obj.data.materials:
|
|
if m == mat:
|
|
count += 1
|
|
return count
|
|
|
|
def get_material_cache(self, mat, by_id=None):
|
|
"""Returns the material cache for this material.
|
|
|
|
Fetches the material cache for the material. Returns None if the material is not in the cache.
|
|
"""
|
|
|
|
mat_cache: CC3MaterialCache
|
|
if mat is not None:
|
|
for mat_cache in self.eye_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.hair_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.head_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.skin_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.tongue_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.teeth_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.tearline_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.eye_occlusion_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.pbr_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
for mat_cache in self.sss_material_cache:
|
|
if mat_cache.material == mat:
|
|
return mat_cache
|
|
|
|
if by_id:
|
|
for mat_cache in self.eye_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.hair_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.head_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.skin_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.tongue_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.teeth_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.tearline_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.eye_occlusion_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.pbr_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
for mat_cache in self.sss_material_cache:
|
|
if mat_cache.material_id == by_id:
|
|
return mat_cache
|
|
return None
|
|
|
|
|
|
def add_or_reuse_material_cache(self, collection):
|
|
for i in range(0, len(collection)):
|
|
if collection[i].material is None:
|
|
utils.log_info(f"Reusing material cache: {str(i)}")
|
|
return collection[i]
|
|
return collection.add()
|
|
|
|
|
|
def get_material_cache_collection(self, material_type):
|
|
if material_type == "DEFAULT" or material_type == "SCALP" or material_type == "EYELASH":
|
|
return self.pbr_material_cache
|
|
elif material_type == "SSS":
|
|
return self.sss_material_cache
|
|
elif material_type == "SKIN_HEAD":
|
|
return self.head_material_cache
|
|
elif (material_type == "SKIN_BODY" or material_type == "SKIN_ARM" or
|
|
material_type == "SKIN_LEG" or material_type == "NAILS"):
|
|
return self.skin_material_cache
|
|
elif material_type == "TEETH_UPPER" or material_type == "TEETH_LOWER":
|
|
return self.teeth_material_cache
|
|
elif material_type == "TONGUE":
|
|
return self.tongue_material_cache
|
|
elif material_type == "HAIR":
|
|
return self.hair_material_cache
|
|
elif (material_type == "CORNEA_RIGHT" or material_type == "CORNEA_LEFT" or
|
|
material_type == "EYE_RIGHT" or material_type == "EYE_LEFT"):
|
|
return self.eye_material_cache
|
|
elif material_type == "OCCLUSION_RIGHT" or material_type == "OCCLUSION_LEFT":
|
|
return self.eye_occlusion_material_cache
|
|
elif material_type == "OCCLUSION_PLUS_RIGHT" or material_type == "OCCLUSION_PLUS_LEFT":
|
|
return self.eye_occlusion_material_cache
|
|
elif material_type == "TEARLINE_RIGHT" or material_type == "TEARLINE_LEFT":
|
|
return self.tearline_material_cache
|
|
elif material_type == "TEARLINE_PLUS_RIGHT" or material_type == "TEARLINE_PLUS_LEFT":
|
|
return self.tearline_material_cache
|
|
else:
|
|
return self.pbr_material_cache
|
|
|
|
|
|
def add_material_cache(self, mat,
|
|
create_type = "DEFAULT",
|
|
is_user=False,
|
|
copy_from=None):
|
|
|
|
"""Returns the material cache for this material.
|
|
|
|
Fetches the material cache for the material. Returns None if the material is not in the cache.
|
|
"""
|
|
|
|
if copy_from:
|
|
create_type = copy_from.material_type
|
|
|
|
mat_cache: CC3MaterialCache = self.get_material_cache(mat)
|
|
if mat_cache is None and mat:
|
|
utils.log_info(f"Creating Material Cache for: {mat.name} (type = {create_type})")
|
|
collection = self.get_material_cache_collection(create_type)
|
|
mat_cache = self.add_or_reuse_material_cache(collection)
|
|
mat_cache.material_type = create_type
|
|
mat_cache.material_id = utils.generate_random_id(20)
|
|
if copy_from:
|
|
utils.log_info(f"Copying material cache from: {copy_from}")
|
|
utils.copy_property_group(copy_from, mat_cache)
|
|
if is_user:
|
|
mat_cache.user_added = True
|
|
mat_cache.material_id = utils.generate_random_id(20)
|
|
mat_cache.material = mat
|
|
mat_cache.source_name = utils.strip_name(mat.name)
|
|
mat_cache.check_id()
|
|
return mat_cache
|
|
|
|
def update_all_properties(self, context=None):
|
|
if not context:
|
|
context = bpy.context
|
|
update_all_properties(context)
|
|
|
|
def get_json_data(self):
|
|
errors = []
|
|
return jsonutils.read_json(self.import_file, errors)
|
|
|
|
def write_json_data(self, json_data, is_local=False):
|
|
jsonutils.write_json(json_data, self.import_file, is_fbx_path=True, update_cache=True, is_json_local=is_local)
|
|
|
|
def change_import_file(self, filepath):
|
|
self.import_file = filepath
|
|
|
|
def get_character_json_path(self):
|
|
return jsonutils.get_json_path(self.import_file)
|
|
|
|
def get_character_json(self, json_data=None):
|
|
if not json_data:
|
|
json_data = self.get_json_data()
|
|
return jsonutils.get_character_json(json_data, self.get_character_id())
|
|
|
|
def get_expression_json(self, json_data=None):
|
|
if not json_data:
|
|
json_data = self.get_json_data()
|
|
chr_json = self.get_character_json(json_data=json_data)
|
|
if chr_json and "Expression" in chr_json:
|
|
expression_json = chr_json["Expression"]
|
|
return copy.deepcopy(expression_json)
|
|
|
|
facial_profile, viseme_profile = self.get_facial_profile()
|
|
if facial_profile == "MH":
|
|
# MH profile has complete expression json
|
|
#default_expression_json = copy.deepcopy(facerig_data.EXPRESSION_MH)
|
|
default_expression_json = {}
|
|
elif facial_profile == "STD" or facial_profile == "EXT":
|
|
default_expression_json = copy.deepcopy(facerig_data.EXPRESSION_EXT)
|
|
elif facial_profile == "TRA":
|
|
default_expression_json = copy.deepcopy(facerig_data.EXPRESSION_TRA)
|
|
else:
|
|
default_expression_json = {}
|
|
return default_expression_json
|
|
|
|
def get_constraint_json(self, json_data=None):
|
|
if not json_data:
|
|
json_data = self.get_json_data()
|
|
chr_json = self.get_character_json(json_data=json_data)
|
|
if chr_json and "Constraint" in chr_json:
|
|
return chr_json["Constraint"]
|
|
return None
|
|
|
|
def check_ids(self):
|
|
rig = self.get_armature()
|
|
# ensure link id is on rig
|
|
if not utils.get_rl_link_id(rig):
|
|
if not self.link_id:
|
|
self.link_id = utils.generate_random_id(20)
|
|
utils.set_rl_link_id(rig, self.link_id)
|
|
# if rigified, ensure the face rig type is on the rig
|
|
if self.rigified:
|
|
if not utils.get_prop(rig, "rl_face_rig"):
|
|
bone_collection = rig.data.edit_bones if utils.get_mode() == "EDIT" else rig.pose.bones
|
|
if "facerig" in bone_collection:
|
|
self.rigify_expression_rig = "META"
|
|
elif "nose" in bone_collection:
|
|
self.rigify_expression_rig = "RIGIFY"
|
|
else:
|
|
self.rigify_expression_rig = "NONE"
|
|
utils.set_prop(rig, "rl_face_rig", self.rigify_expression_rig)
|
|
# ensure the facial profile & viseme profile types are in the character data
|
|
self.get_facial_profile()
|
|
|
|
|
|
def recast_type(self, collection, index, chr_json):
|
|
mat_cache = collection[index]
|
|
mat = mat_cache.material
|
|
utils.log_info(f"Recasting material cache: {mat.name}")
|
|
material_type = mat_cache.material_type
|
|
mat["rl_material_type"] = material_type
|
|
mat_cache.material = None
|
|
mat_cache.source_name = ""
|
|
new_mat_cache = self.add_material_cache(mat, material_type)
|
|
if not chr_json:
|
|
chr_json = self.get_character_json()
|
|
for obj_cache in self.object_cache:
|
|
obj = obj_cache.get_object()
|
|
if obj_cache.is_mesh():
|
|
for m in obj.data.materials:
|
|
if m and m == mat:
|
|
new_mat_cache.dir = imageutils.get_material_tex_dir(self, obj, mat)
|
|
obj_json = jsonutils.get_object_json(chr_json, obj)
|
|
mat_json = jsonutils.get_material_json(obj_json, mat)
|
|
init_material_property_defaults(obj, mat, obj_cache, new_mat_cache, obj_json, mat_json)
|
|
utils.log_info("Recast Complete.")
|
|
return
|
|
|
|
def check_type(self, collection, recast, chr_json, *types):
|
|
for i in range(0, len(collection)):
|
|
if i < len(collection):
|
|
if collection[i].material and collection[i].material_type not in types:
|
|
self.recast_type(collection, i, chr_json)
|
|
i -= 1
|
|
|
|
def check_material_types(self, chr_json):
|
|
recast = []
|
|
self.check_type(self.tongue_material_cache, recast, chr_json, "TONGUE")
|
|
self.check_type(self.teeth_material_cache, recast, chr_json, "TEETH_LOWER", "TEETH_UPPER")
|
|
self.check_type(self.head_material_cache, recast, chr_json, "SKIN_HEAD")
|
|
self.check_type(self.skin_material_cache, recast, chr_json, "SKIN_BODY", "SKIN_ARM", "SKIN_LEG", "NAILS")
|
|
self.check_type(self.tearline_material_cache, recast, chr_json, "TEARLINE_LEFT", "TEARLINE_RIGHT", "TEARLINE_PLUS_LEFT", "TEARLINE_PLUS_RIGHT")
|
|
self.check_type(self.eye_occlusion_material_cache, recast, chr_json, "OCCLUSION_RIGHT", "OCCLUSION_LEFT", "OCCLUSION_PLUS_RIGHT", "OCCLUSION_PLUS_LEFT")
|
|
self.check_type(self.eye_material_cache, recast, chr_json, "CORNEA_RIGHT", "CORNEA_LEFT", "EYE_RIGHT", "EYE_LEFT")
|
|
self.check_type(self.hair_material_cache, recast, chr_json, "HAIR")
|
|
self.check_type(self.pbr_material_cache, recast, chr_json, "DEFAULT", "SCALP", "EYELASH")
|
|
self.check_type(self.sss_material_cache, recast, chr_json, "SSS")
|
|
|
|
def is_wrinkle_active(self):
|
|
mat_cache: CC3MaterialCache = None
|
|
for mat_cache in self.head_material_cache:
|
|
if utils.material_exists(mat_cache.material):
|
|
mat = mat_cache.material
|
|
nodes = mat.node_tree.nodes
|
|
for node in nodes:
|
|
if "(rl_wrinkle_shader)" in node.name:
|
|
return True
|
|
return False
|
|
|
|
def get_sculpt_objects(self):
|
|
sculpt_objects = self.get_all_objects(include_armature=False,
|
|
include_cache=False,
|
|
include_children=False,
|
|
include_disabled=False,
|
|
include_split=False,
|
|
include_proxy=False,
|
|
include_sculpt=True,
|
|
of_type="MESH")
|
|
return sculpt_objects
|
|
|
|
def get_sculpt_source(self, multires_mesh, layer_target):
|
|
prop_name = f"rl_multires_{layer_target}"
|
|
if prop_name in multires_mesh:
|
|
source_name = multires_mesh[prop_name]
|
|
if source_name in bpy.data.objects:
|
|
return bpy.data.objects[source_name]
|
|
return None
|
|
|
|
def get_detail_body(self, context_object=None):
|
|
sculpt_objects = self.get_sculpt_objects()
|
|
if context_object:
|
|
for obj in sculpt_objects:
|
|
source = self.get_sculpt_source(obj, sculpting.LAYER_TARGET_DETAIL)
|
|
if obj == context_object or source == context_object:
|
|
return obj
|
|
return None
|
|
# detail_multires_body contains the last edited detail sculpt object
|
|
if utils.object_exists_is_mesh(self.detail_multires_body):
|
|
return self.detail_multires_body
|
|
else:
|
|
return None
|
|
|
|
def get_sculpt_body(self, context_object=None):
|
|
sculpt_objects = self.get_sculpt_objects()
|
|
if context_object:
|
|
for obj in sculpt_objects:
|
|
source = self.get_sculpt_source(obj, sculpting.LAYER_TARGET_SCULPT)
|
|
if obj == context_object or source == context_object:
|
|
return obj
|
|
return None
|
|
# sculpt_multires_body contains the last edited body sculpt object
|
|
if utils.object_exists_is_mesh(self.sculpt_multires_body):
|
|
return self.sculpt_multires_body
|
|
else:
|
|
return None
|
|
|
|
def set_detail_body(self, mesh):
|
|
self.detail_multires_body = mesh
|
|
|
|
def set_sculpt_body(self, mesh):
|
|
self.sculpt_multires_body = mesh
|
|
|
|
def get_related_physics_objects(self, obj):
|
|
proxy = None
|
|
is_proxy = False
|
|
obj_cache = self.get_object_cache(obj)
|
|
if obj_cache:
|
|
proxy = self.get_collision_proxy(obj)
|
|
if proxy:
|
|
is_proxy = False
|
|
return obj, proxy, is_proxy
|
|
if "rl_collision_proxy" in obj or obj.name.endswith(".Collision_Proxy"):
|
|
proxy = obj
|
|
is_proxy = True
|
|
obj = self.find_object_from_proxy(proxy)
|
|
return obj, proxy, is_proxy
|
|
return obj, proxy, is_proxy
|
|
|
|
def find_object_from_proxy(self, proxy, include_disabled=False):
|
|
if "rl_collision_proxy" in proxy:
|
|
proxy_object_id = utils.get_rl_object_id(proxy)
|
|
for obj in self.get_cache_objects():
|
|
obj_cache = self.get_object_cache(obj)
|
|
if include_disabled or not obj_cache.disabled:
|
|
if utils.get_rl_object_id(obj) == proxy_object_id and obj.name == proxy["rl_collision_proxy"]:
|
|
return obj
|
|
if proxy.name.endswith(".Collision_Proxy"):
|
|
obj_name = proxy.name[:-16]
|
|
for obj in self.get_cache_objects():
|
|
obj_cache = self.get_object_cache(obj)
|
|
if include_disabled or not obj_cache.disabled:
|
|
if obj.name == obj_name:
|
|
return obj
|
|
return None
|
|
|
|
def is_valid_for_export(self):
|
|
"""If the character is a standard cc3+ character, is the body topology valid for export"""
|
|
if self.is_standard():
|
|
body = None
|
|
body_cache = None
|
|
for obj_cache in self.object_cache:
|
|
if obj_cache.object_type == "BODY":
|
|
body = obj_cache.get_object()
|
|
if body:
|
|
body_cache = obj_cache
|
|
break
|
|
else:
|
|
body_cache = None
|
|
if body and body_cache:
|
|
if self.generation == "G3":
|
|
if (len(body.data.vertices) == 13286 and
|
|
len(body.data.edges) == 26374 and
|
|
len(body.data.polygons) == 13100):
|
|
return True
|
|
elif self.generation == "G3Plus":
|
|
# level 0
|
|
if (len(body.data.vertices) == 14164 and
|
|
len(body.data.edges) == 28202 and
|
|
len(body.data.polygons) == 14046):
|
|
return True
|
|
# level 1
|
|
if (len(body.data.vertices) == 56412 and
|
|
len(body.data.edges) == 112588 and
|
|
len(body.data.polygons) == 56184):
|
|
return True
|
|
# level 2
|
|
if (len(body.data.vertices) == 225184 and
|
|
len(body.data.edges) == 449912 and
|
|
len(body.data.polygons) == 224736):
|
|
return True
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def get_link_id(self):
|
|
if not self.link_id:
|
|
self.link_id = utils.generate_random_id(20)
|
|
return self.link_id
|
|
|
|
def validate(self, report=None):
|
|
"""Checks character objects and materials are still valid.
|
|
Returns True if any objects in the character are still valid"""
|
|
obj_cache: CC3ObjectCache
|
|
mat_cache: CC3MaterialCache
|
|
any_valid = False
|
|
for obj_cache in self.object_cache:
|
|
split_objects = self.get_split_objects(obj_cache)
|
|
obj_valid = obj_cache.validate(report=report, split_objects=split_objects)
|
|
any_valid = any_valid or obj_valid
|
|
all_materials_cache = self.get_all_materials_cache(True)
|
|
for mat_cache in all_materials_cache:
|
|
mat_valid = mat_cache.validate(report)
|
|
if not any_valid:
|
|
rep = f"Character Cache: {self.character_name} is no longer valid!"
|
|
utils.log_info(rep)
|
|
if report is not None:
|
|
report.append(rep)
|
|
self.invalidate()
|
|
return not self.disabled
|
|
|
|
def invalidate(self):
|
|
utils.log_info(f"Invalidating Character cache: {self.character_name}")
|
|
self.disabled = True
|
|
obj_cache: CC3ObjectCache
|
|
mat_cache: CC3MaterialCache
|
|
for obj_cache in self.object_cache:
|
|
obj_cache.invalidate()
|
|
all_materials_cache = self.get_all_materials_cache(include_disabled=True)
|
|
for mat_cache in all_materials_cache:
|
|
mat_cache.invalidate()
|
|
|
|
def delete(self):
|
|
obj_cache: CC3ObjectCache
|
|
mat_cache: CC3MaterialCache
|
|
for obj in self.get_cache_objects():
|
|
proxy = self.get_collision_proxy(obj)
|
|
if proxy:
|
|
utils.delete_object_tree(proxy)
|
|
for obj_cache in self.object_cache:
|
|
obj_cache.delete()
|
|
all_materials_cache = self.get_all_materials_cache(include_disabled=True)
|
|
for mat_cache in all_materials_cache:
|
|
mat_cache.delete()
|
|
if self.disabled:
|
|
utils.log_info(f"Deleting Character Meta Objects: {self.character_name}")
|
|
utils.delete_object_tree(self.rig_meta_rig)
|
|
utils.delete_object_tree(self.arkit_proxy)
|
|
utils.delete_object_tree(self.rig_export_rig)
|
|
utils.delete_object_tree(self.rig_original_rig)
|
|
utils.delete_object_tree(self.rig_retarget_rig)
|
|
utils.delete_object_tree(self.rig_datalink_rig)
|
|
utils.delete_object_tree(self.rig_retarget_source_rig)
|
|
utils.delete_object(self.detail_multires_body)
|
|
utils.delete_object(self.sculpt_multires_body)
|
|
|
|
def clean_up(self):
|
|
obj_cache: CC3ObjectCache
|
|
mat_cache: CC3MaterialCache
|
|
for obj_cache in self.object_cache:
|
|
obj_cache.clean_up()
|
|
all_materials_cache = self.get_all_materials_cache(include_disabled=True)
|
|
for mat_cache in all_materials_cache:
|
|
mat_cache.clean_up()
|
|
if self.disabled:
|
|
utils.log_detail(f"Clearing object cache.")
|
|
self.object_cache.clear()
|
|
utils.log_detail(f"Clearing all material cache.")
|
|
self.tongue_material_cache.clear()
|
|
self.teeth_material_cache.clear()
|
|
self.head_material_cache.clear()
|
|
self.skin_material_cache.clear()
|
|
self.tearline_material_cache.clear()
|
|
self.eye_occlusion_material_cache.clear()
|
|
self.eye_material_cache.clear()
|
|
self.hair_material_cache.clear()
|
|
self.pbr_material_cache.clear()
|
|
self.sss_material_cache.clear()
|
|
self.proportion_editing_actions.clear()
|
|
else:
|
|
utils.log_detail(f"Cleaning up object cache.")
|
|
clean_collection_property(self.object_cache)
|
|
utils.log_detail(f"Cleaning up all material cache.")
|
|
clean_collection_property(self.tongue_material_cache)
|
|
clean_collection_property(self.teeth_material_cache)
|
|
clean_collection_property(self.head_material_cache)
|
|
clean_collection_property(self.skin_material_cache)
|
|
clean_collection_property(self.tearline_material_cache)
|
|
clean_collection_property(self.eye_occlusion_material_cache)
|
|
clean_collection_property(self.eye_material_cache)
|
|
clean_collection_property(self.hair_material_cache)
|
|
clean_collection_property(self.pbr_material_cache)
|
|
clean_collection_property(self.sss_material_cache)
|
|
# endregion
|
|
|
|
# region ImportProps
|
|
class CC3ImportProps(bpy.types.PropertyGroup):
|
|
|
|
node_id: bpy.props.IntProperty(default=1000)
|
|
|
|
build_mode: bpy.props.EnumProperty(items=[
|
|
("IMPORTED","All Imported","Rebuild materials for all the imported objects."),
|
|
("SELECTED","Only Selected","Rebuild materials only for the selected objects.")
|
|
], default="IMPORTED")
|
|
|
|
blend_mode: bpy.props.EnumProperty(items=[
|
|
("BLEND","Alpha Blend","Setup any non opaque materials as basic Alpha Blend"),
|
|
("HASHED","Alpha Hashed","Setup non opaque materials as alpha hashed (Resolves Z sorting issues, but may need more samples)")
|
|
], default="HASHED")
|
|
|
|
update_mode: bpy.props.EnumProperty(items=[
|
|
("UPDATE_LINKED","Linked","Update the shader parameters for all materials of the same type in all the objects from the last import"),
|
|
("UPDATE_SELECTED","Selected","Update the shader parameters only in the selected object and material")
|
|
], default="UPDATE_LINKED")
|
|
|
|
setup_mode: bpy.props.EnumProperty(items=[
|
|
("BASIC","Basic","Build basic PBR materials."),
|
|
("ADVANCED","Advanced","Build advanced materials with blend maps, subsurface, and micro normals, specular and roughness control and includes layered eye, teeth and tongue materials.")
|
|
], default="ADVANCED")
|
|
|
|
import_file: bpy.props.StringProperty(default="", subtype="FILE_PATH")
|
|
|
|
import_cache: bpy.props.CollectionProperty(type=CC3CharacterCache)
|
|
|
|
dummy_slider: bpy.props.FloatProperty(default=0.5, min=0, max=1)
|
|
|
|
unity_file_path: bpy.props.StringProperty(default="", subtype="FILE_PATH")
|
|
unity_project_path: bpy.props.StringProperty(default="", subtype="FILE_PATH")
|
|
|
|
physics_paint_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=physics.physics_paint_strength_update)
|
|
weight_map_strength: bpy.props.FloatProperty(default=1.0, min=0, max=1, update=physics.weight_strength_update)
|
|
physics_tex_size: bpy.props.EnumProperty(items=[
|
|
("64","64 x 64","64 x 64 texture size"),
|
|
("128","128 x 128","128 x 128 texture size"),
|
|
("256","256 x 256","256 x 256 texture size"),
|
|
("512","512 x 512","512 x 512 texture size"),
|
|
("1024","1024 x 1024","1024 x 1024 texture size"),
|
|
("2048","2048 x 2048","2048 x 2048 texture size"),
|
|
("4096","4096 x 4096","4096 x 4096 texture size"),
|
|
], default="1024")
|
|
|
|
paint_object: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
paint_material: bpy.props.PointerProperty(type=bpy.types.Material)
|
|
paint_image: bpy.props.PointerProperty(type=bpy.types.Image)
|
|
paint_store_render: bpy.props.StringProperty(default="")
|
|
|
|
quick_set_mode: bpy.props.EnumProperty(items=[
|
|
("OBJECT","Object","Set the alpha blend mode and backface culling to all materials on the selected object(s)"),
|
|
("MATERIAL","Material","Set the alpha blend mode and backface culling only to the selected material on the active object"),
|
|
], default="MATERIAL")
|
|
|
|
lighting_mode: bpy.props.BoolProperty(default=False,
|
|
description="Automatically sets lighting and render settings, depending on use.")
|
|
physics_mode: bpy.props.BoolProperty(default=False,
|
|
description="Automatically generates physics vertex groups and settings.")
|
|
wrinkle_mode: bpy.props.BoolProperty(default=True,
|
|
description="Automatically generates wrinkle maps for this character (if available).")
|
|
rigify_mode: bpy.props.BoolProperty(default=False,
|
|
description="Automatically rigify the character and retarget any animations or poses that came with the character.")
|
|
|
|
export_options: bpy.props.BoolProperty(default=False)
|
|
cycles_options1: bpy.props.BoolProperty(default=False)
|
|
eevee_options1: bpy.props.BoolProperty(default=False)
|
|
cycles_options2: bpy.props.BoolProperty(default=False)
|
|
eevee_options2: bpy.props.BoolProperty(default=False)
|
|
stage1: bpy.props.BoolProperty(default=True)
|
|
stage1_details: bpy.props.BoolProperty(default=False)
|
|
stage4: bpy.props.BoolProperty(default=True)
|
|
stage_remapper: bpy.props.BoolProperty(default=False)
|
|
show_build_prefs: bpy.props.BoolProperty(default=False)
|
|
show_build_prefs2: bpy.props.BoolProperty(default=False)
|
|
section_rigify_setup: bpy.props.BoolProperty(default=True)
|
|
section_rigify_retarget: bpy.props.BoolProperty(default=True)
|
|
section_rigify_action_sets: bpy.props.BoolProperty(default=True)
|
|
section_rigify_arkit: bpy.props.BoolProperty(default=True)
|
|
section_rigify_controls: bpy.props.BoolProperty(default=True)
|
|
section_rigify_spring: bpy.props.BoolProperty(default=False)
|
|
section_rigidbody_spring_ui: bpy.props.BoolProperty(default=True)
|
|
section_physics_cloth_settings: bpy.props.BoolProperty(default=False)
|
|
section_physics_collision_settings: bpy.props.BoolProperty(default=False)
|
|
show_data_link_prefs: bpy.props.BoolProperty(default=False)
|
|
section_rigify_export: bpy.props.BoolProperty(default=True)
|
|
|
|
skin_toggle: bpy.props.BoolProperty(default=True)
|
|
eye_toggle: bpy.props.BoolProperty(default=True)
|
|
teeth_toggle: bpy.props.BoolProperty(default=True)
|
|
tongue_toggle: bpy.props.BoolProperty(default=True)
|
|
nails_toggle: bpy.props.BoolProperty(default=True)
|
|
hair_toggle: bpy.props.BoolProperty(default=True)
|
|
default_toggle: bpy.props.BoolProperty(default=True)
|
|
|
|
section_hair_blender_curve: bpy.props.BoolProperty(default=True)
|
|
section_hair_rigging: bpy.props.BoolProperty(default=True)
|
|
|
|
section_sculpt_setup: bpy.props.BoolProperty(default=True)
|
|
section_sculpt_cleanup: bpy.props.BoolProperty(default=True)
|
|
section_sculpt_utilities: bpy.props.BoolProperty(default=True)
|
|
sculpt_layer_tab: bpy.props.EnumProperty(items=[
|
|
("BODY","Body","Full body sculpt layer.", "OUTLINER_OB_ARMATURE", 0),
|
|
("DETAIL","Detail","Detail sculpt layer.", "MESH_MONKEY", 1),
|
|
], default="BODY", name = "Sculpt Layer")
|
|
|
|
geom_transfer_layer: bpy.props.EnumProperty(items=[
|
|
("BASE","Base","Transfer geometry to the base mesh."),
|
|
("SHAPE_KEY","Shape Key","Transfer geometry to a new shape key. This will *not* alter the bones."),
|
|
], default="BASE", name = "Transfer Layer")
|
|
|
|
geom_transfer_layer_name: bpy.props.StringProperty(name="Name", default="New Shape",
|
|
description="Name to assign to transferred shape key")
|
|
|
|
|
|
# rigify
|
|
rigify_retarget_use_fake_user: bpy.props.BoolProperty(default=True, name="Fake User")
|
|
rigify_retarget_motion_prefix: bpy.props.StringProperty(default="", name="Rigify Retarget Motion Prefix",
|
|
description="Motion prefix for retargeted motions.")
|
|
rigify_bake_use_fake_user: bpy.props.BoolProperty(default=True, name="Fake User")
|
|
rigify_bake_motion_prefix: bpy.props.StringProperty(default="", name="Rigify Bake Motion Prefix",
|
|
description="Motion prefix for baked NLA motions.")
|
|
rigify_bake_motion_name: bpy.props.StringProperty(default="NLA_Bake", name="Rigify Bake Motion Name",
|
|
description="Motion name for baked NLA motions.")
|
|
filter_motion_set_type: bpy.props.EnumProperty(items=[
|
|
("NONE","None","No Filter"),
|
|
("RIG","Rig","Filter for selected Rig"),
|
|
("GEN","Type","Filter for selected Rig type (Generation)"),
|
|
], default="RIG", name = "Filter",
|
|
description="Motion set filter type")
|
|
|
|
# Hair
|
|
|
|
hair_export_group_by: bpy.props.EnumProperty(items=[
|
|
("CURVE","Curve","Group by curve objects"),
|
|
("NAME","Name","Group by name"),
|
|
("NONE","Single","Don't export separate groups"),
|
|
], default="CURVE", name = "Export Hair Grouping",
|
|
description="Export hair groups by...")
|
|
|
|
hair_card_dir_threshold: bpy.props.FloatProperty(default=0.9, min=0.0, max=1.0, name="Direction Threshold")
|
|
hair_card_vertical_dir: bpy.props.EnumProperty(items=[
|
|
("DOWN","Down","Hair cards from top to bottom in UV map", "SORT_ASC", 0),
|
|
("UP","Up","Hair cards from bottom to top in UV map", "SORT_DESC", 1),
|
|
], default="DOWN", name = "UV Direction",
|
|
description="Direction of vertical hair cards in UV Map")
|
|
hair_card_horizontal_dir: bpy.props.EnumProperty(items=[
|
|
("RIGHT","Right","Hair cards from left to right in UV map", "FORWARD", 2),
|
|
("LEFT","Left","Hair cards from right to left in UV map", "BACK", 3),
|
|
], default="RIGHT", name = "UV Direction",
|
|
description="Direction of horizontal hair cards in UV Map")
|
|
hair_card_square_dir: bpy.props.EnumProperty(items=[
|
|
("DOWN","Down","Hair cards from top to bottom in UV map", "SORT_ASC", 0),
|
|
("UP","Up","Hair cards from bottom to top in UV map", "SORT_DESC", 1),
|
|
("RIGHT","Right","Hair cards from left to right in UV map", "FORWARD", 2),
|
|
("LEFT","Left","Hair cards from right to left in UV map", "BACK", 3),
|
|
], default="DOWN", name = "UV Direction",
|
|
description="Direction of square(ish) hair cards in UV Map")
|
|
hair_curve_merge_loops: bpy.props.EnumProperty(items=[
|
|
("ALL","Use All Edge Loops","All edge loops in the cards will be converted into curves"),
|
|
("MERGE","Merge Edge Loops","Edge loops in each card will be merged into a single curve"),
|
|
], default="MERGE", name = "Merge Loops",
|
|
description="Merge edge loops")
|
|
|
|
hair_rig_bone_smoothing: bpy.props.IntProperty(default=5, min=0, max=10,
|
|
description="How much to smooth the curve of the generated bones from hair cards or greased pencil")
|
|
hair_rig_bind_skip_length: bpy.props.FloatProperty(default=3.75, min=0.0, max=20.0,
|
|
description="How far along the hair card to start generating bones, "
|
|
"as rooting the bones to the very start of the hair cards can produce unwanted results")
|
|
hair_rig_bind_trunc_length: bpy.props.FloatProperty(default=2.5, min=0.0, max=10.0,
|
|
description="How far from the end of the hair card to stop generating bones")
|
|
hair_rig_bone_length: bpy.props.FloatProperty(default=7.5, min=2.5, max=25,
|
|
description="How long a section of each hair card the bones should represent")
|
|
hair_rig_bind_bone_radius: bpy.props.FloatProperty(default=7.5, min=1, max=25,
|
|
description="How wide a radius around the bones should the hair cards bind vertex weights to")
|
|
hair_rig_bind_bone_count: bpy.props.IntProperty(default=2, min=1, max=4,
|
|
description="How many neighouring bones should each hair card bind to.\n\n"
|
|
"Note: More bones may produce smoother results but add to the overall mesh skinning performance cost")
|
|
hair_rig_bind_bone_weight: bpy.props.FloatProperty(default=1.0, min=0.0, max=1.0,
|
|
description="How much to scale the generated weights by")
|
|
hair_rig_bind_bone_variance: bpy.props.FloatProperty(default=0.85, min=0.0, max=1.0,
|
|
description="How much random variation in the generated weights.\n\n"
|
|
"Less variance will cause all the hair cards to the follow the bones more closely.\n\n"
|
|
"More variance will cause a wider spread of the cards as the bones move which gives the appearance of more volume")
|
|
hair_rig_bind_existing_scale: bpy.props.FloatProperty(default=0.1, min=0.01, max=1.0,
|
|
description="How much to scale any existing body weights on the hair.\n\n"
|
|
"Note: The spring bones vertex weights will compete with the body vertex weights. Scaling the body weights back (< 1.0) "
|
|
"will allow the hair to follow the spring bones more closely but will then conform less to the body")
|
|
hair_rig_bind_weight_curve: bpy.props.FloatProperty(default=0.5, min=0.25, max=4.0,
|
|
description="How to fade in the bone weights of each hair card from root to ends.\n\n"
|
|
"Larger values ( > 1.0) will push the weights down closer to the ends.\n\n"
|
|
"Smaller values ( < 1.0) will push the weights up closer to the roots")
|
|
hair_rig_bind_smoothing: bpy.props.IntProperty(default=5, min=0, max=10,
|
|
description="How much to smooth the generated weights after binding")
|
|
hair_rig_bind_seed: bpy.props.IntProperty(default=1, min=1, max=10000,
|
|
description="The random seed for generating the weight variance. The same seed should produce the same results each time")
|
|
hair_rig_bind_card_mode: bpy.props.EnumProperty(items=[
|
|
("ALL","All Cards","Bind all hair cards in the selected objects"),
|
|
("SELECTED","Selected Cards","Bind only the selected hair cards in each selected object"),
|
|
], default="ALL", name = "Hair Card Selection Mode")
|
|
hair_rig_bind_bone_mode: bpy.props.EnumProperty(items=[
|
|
("ALL","All Bones","Operate on all bones in the hair rig"),
|
|
("SELECTED","Selected Bones","Operate on only the currently selected bones of the hair rig"),
|
|
], default="ALL", name = "Bone Selection Mode")
|
|
hair_rig_bone_root: bpy.props.EnumProperty(items=[
|
|
("HEAD","Head Hair","Parent generated bones to the head bone"),
|
|
("JAW","Beard Hair","Parent the generated bones to the jaw bone, for beards"),
|
|
], default="HEAD", name = "Root bone for generated hair bones")
|
|
hair_rig_target: bpy.props.EnumProperty(items=[
|
|
("BLENDER","Blender","Generate a spring rig for Blender"),
|
|
("CC4","CC4","Generate a compatible spring rig for Character Creator and iClone.\n"
|
|
"For Character Creator spring rigs, all other vertex weights are removed, and the first bone of each chain is fixed in place."),
|
|
("UNITY","Unity","Generate a spring rig for Unity"),
|
|
], default="BLENDER", name = "Rig Target Application", update=update_rig_target)
|
|
|
|
hair_rig_group_name: bpy.props.StringProperty(name="Group Name", default="RL_Hair",
|
|
description="Name to assign to selected bone chains as a separate group")
|
|
|
|
hair_rigid_body_influence: bpy.props.FloatProperty(default=1.0, min=0.0, max=1.0, name = "Influence",
|
|
description = "How much of the simulation is copied into the pose bones")
|
|
hair_rigid_body_limit: bpy.props.FloatProperty(default=25, min=0, max=50, name = "Rigid Body Dampening Range",
|
|
description = "How big a dampening range to apply to the rigid body. More range gives more movement")
|
|
hair_rigid_body_curve: bpy.props.FloatProperty(default=0.5, min=1/8, max=2, name = "Length Dampening Curve",
|
|
description = "The dampening curve factor along the length of the spring bone chains. Less curve gives more movement near the roots")
|
|
hair_rigid_body_mass: bpy.props.FloatProperty(default=1.0, min=0.0, max=5.0, name = "Hair Node Mass",
|
|
description = "Mass of the rigid body particles representing the bones. More mass, more inertia")
|
|
hair_rigid_body_dampening: bpy.props.FloatProperty(default=10.0, min=0.0, max=10000.0, name = "Spring Dampening",
|
|
description = "Spring dampening. (Makes very little difference)")
|
|
hair_rigid_body_stiffness: bpy.props.FloatProperty(default=50.0, min=0.0, max=100.0, name = "Spring Stiffness",
|
|
description = "Spring stiffness. (Makes very little difference)")
|
|
hair_rigid_body_radius: bpy.props.FloatProperty(default=0.05, min=0.0125, max=0.1, name = "Hair Node Collision Radius",
|
|
description = "Collision radius of the rigid body partivles representing the bones. Note: Too much and the rigid body system will start colliding with itself")
|
|
|
|
# UI List props
|
|
armature_action_filter: bpy.props.BoolProperty(default=True)
|
|
action_list_index: bpy.props.IntProperty(default=-1)
|
|
action_list_action: bpy.props.PointerProperty(type=bpy.types.Action)
|
|
action_set_list_index: bpy.props.IntProperty(default=-1)
|
|
action_set_list_action: bpy.props.PointerProperty(type=bpy.types.Action)
|
|
armature_list_index: bpy.props.IntProperty(default=-1)
|
|
armature_list_object: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
unity_action_list_index: bpy.props.IntProperty(default=-1)
|
|
unity_action_list_action: bpy.props.PointerProperty(type=bpy.types.Action)
|
|
rigified_action_list_index: bpy.props.IntProperty(default=-1)
|
|
rigified_action_list_action: bpy.props.PointerProperty(type=bpy.types.Action)
|
|
|
|
wrinkle_regions: bpy.props.EnumProperty(items=[
|
|
("ALL", "All", "All Regions"),
|
|
("01", " 1 - Brow Raise", "Brow Raise"),
|
|
("02", " 2 - Brow Drop", "Brow Drop"),
|
|
("03", " 3 - Blink", "Blink"),
|
|
("04", " 4 - Squint", "Squint"),
|
|
("05", " 5 - Nose", "Nose"),
|
|
("06", " 6 - Cheek Raise", "Cheek Raise"),
|
|
("07", " 7 - Nostril Crease", "Nostril Crease"),
|
|
("08", " 8 - Purse Lips", "Purse Lips"),
|
|
("09", " 9 - Smile Lip Stretch", "Smile Lip Stretch"),
|
|
("10", "10 - Mouth Stretch", "Mouth Stretch"),
|
|
("11", "11 - Chin", "Chin"),
|
|
("12", "12 - Jaw", "Jaw"),
|
|
("13", "13 - Neck Stretch", "Neck Stretch"),
|
|
], default="ALL", name = "Wrinkle Region")
|
|
wrinkle_strength: bpy.props.FloatProperty(default=1.0, min=0.0, max=2.0, name = "Wrinkle Strength",
|
|
update=update_wrinkle_strength_all)
|
|
wrinkle_curve: bpy.props.FloatProperty(default=1.0, min=0.1, max=2.0, name = "Wrinkle Curve",
|
|
update=update_wrinkle_curve_all)
|
|
|
|
#
|
|
light_filter: bpy.props.FloatVectorProperty(subtype="COLOR", size=4,
|
|
default=(1.0, 1.0, 1.0, 1.0),
|
|
min = 0.0, max = 1.0)
|
|
lighting_setup_compositor: bpy.props.BoolProperty(default=False)
|
|
lighting_setup_camera: bpy.props.BoolProperty(default=False)
|
|
lighting_brightness_all: bpy.props.BoolProperty(default=False,
|
|
name="All Lights",
|
|
description="Adjust all lights with the lighting brightness slider, not just the ones created by this add-on")
|
|
lighting_brightness: bpy.props.FloatProperty(default=1.0, min=0.0, max=2.0,
|
|
name="Lighting Brightness",
|
|
description="Adjust the lighting brightness of all lights created by this add-on",
|
|
update=adjust_lighting_brightness)
|
|
world_brightness: bpy.props.FloatProperty(default=1.0, min=0.0, max=2.0,
|
|
name="World Brightness",
|
|
description="Adjust the world background brightness if the world setup was created by this add-on",
|
|
update=adjust_world_brightness)
|
|
|
|
action_options: bpy.props.PointerProperty(type=CCICActionOptions)
|
|
|
|
|
|
def add_character_cache(self, copy_from=None):
|
|
chr_cache = self.import_cache.add()
|
|
if copy_from:
|
|
exclude_list = ["*_material_cache", "object_cache"]
|
|
utils.copy_property_group(copy_from, chr_cache, exclude=exclude_list)
|
|
return chr_cache
|
|
|
|
def get_character_cache_from_objects(self, objects, search_materials=False):
|
|
chr_cache : CC3CharacterCache
|
|
|
|
if objects:
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled:
|
|
if chr_cache.has_cache_objects(objects):
|
|
return chr_cache
|
|
#if chr_cache.rig_meta_rig and chr_cache.rig_meta_rig in objects:
|
|
# return chr_cache
|
|
for obj in objects:
|
|
if chr_cache.is_related_object(obj):
|
|
return chr_cache
|
|
|
|
if search_materials:
|
|
materials = []
|
|
for obj in objects:
|
|
if obj.type == "MESH":
|
|
for mat in obj.data.materials:
|
|
materials.append(mat)
|
|
if materials:
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled and chr_cache.has_any_materials(materials):
|
|
return chr_cache
|
|
return None
|
|
|
|
def get_character_cache(self, obj, mat, by_id=None):
|
|
if obj:
|
|
if not by_id:
|
|
by_id = utils.get_rl_object_id(obj)
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled:
|
|
obj_cache = chr_cache.get_object_cache(obj, by_id=by_id)
|
|
if obj_cache and not obj_cache.disabled:
|
|
return chr_cache
|
|
if mat:
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled:
|
|
mat_cache = chr_cache.get_material_cache(mat)
|
|
if mat_cache and not mat_cache.disabled:
|
|
return chr_cache
|
|
return None
|
|
|
|
def get_characters_by_link_id(self, character_ids):
|
|
characters = []
|
|
for chr_cache in self.import_cache:
|
|
if chr_cache.link_id in character_ids:
|
|
characters.append(chr_cache)
|
|
return characters
|
|
|
|
def get_avatars(self):
|
|
avatars = []
|
|
chr_cache: CC3CharacterCache
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled and chr_cache.is_avatar():
|
|
avatars.append(chr_cache)
|
|
return avatars
|
|
|
|
def get_first_avatar(self):
|
|
chr_cache: CC3CharacterCache
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled and chr_cache.is_avatar():
|
|
return chr_cache
|
|
return None
|
|
|
|
def find_character_by_name(self, name):
|
|
if name:
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled and chr_cache.character_name == name:
|
|
return chr_cache
|
|
return None
|
|
|
|
def find_character_by_link_id(self, link_id):
|
|
if link_id:
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.disabled and chr_cache.link_id == link_id:
|
|
return chr_cache
|
|
return None
|
|
|
|
def get_context_character_cache(self, context=None, strict=False) -> CC3CharacterCache:
|
|
context = vars.get_context(context)
|
|
|
|
chr_cache = None
|
|
|
|
# if there is only one character in the scene, this is the only possible character cache:
|
|
if not strict and len(self.import_cache) == 1:
|
|
return self.import_cache[0]
|
|
|
|
obj = context.object
|
|
|
|
# otherwise determine the context character cache:
|
|
chr_cache = self.get_character_cache(obj, None)
|
|
|
|
# try to find a character from the selected objects
|
|
if chr_cache is None and len(context.selected_objects) > 1:
|
|
chr_cache = self.get_character_cache_from_objects(context.selected_objects, False)
|
|
|
|
return chr_cache
|
|
|
|
def get_object_cache(self, obj, include_disabled=False):
|
|
if obj:
|
|
for chr_cache in self.import_cache:
|
|
obj_cache = chr_cache.get_object_cache(obj, include_disabled=include_disabled)
|
|
if obj_cache:
|
|
return obj_cache
|
|
return None
|
|
|
|
def get_material_cache(self, mat):
|
|
if mat:
|
|
for imp_cache in self.import_cache:
|
|
mat_cache = imp_cache.get_material_cache(mat)
|
|
if mat_cache:
|
|
return mat_cache
|
|
return None
|
|
|
|
def is_unity_project(self):
|
|
prefs = vars.prefs()
|
|
local_path = utils.local_path()
|
|
if local_path:
|
|
if prefs.export_unity_mode == "BLEND" and self.unity_file_path and self.unity_project_path:
|
|
if utils.is_in_path(self.unity_project_path, local_path):
|
|
return True
|
|
return False
|
|
|
|
def store_actions(self, rig: bpy.types.Object):
|
|
store_id = utils.generate_random_id(30)
|
|
action_store: CCICActionStore = self.action_options.action_store.add()
|
|
action_store.store(rig, store_id)
|
|
if utils.object_exists_is_armature(rig):
|
|
for obj in rig.children:
|
|
if utils.object_has_shape_keys(obj):
|
|
action_store: CCICActionStore = self.action_options.action_store.add()
|
|
action_store.store(obj, store_id)
|
|
return store_id
|
|
|
|
def restore_actions(self, store_id: str):
|
|
action_store: CCICActionStore
|
|
restored = False
|
|
if store_id:
|
|
for action_store in self.action_options.action_store:
|
|
if action_store.store_id == store_id:
|
|
action_store.restore()
|
|
restored = True
|
|
return restored
|
|
|
|
def fetch_stored_rig_action(self, store_id):
|
|
action_store: CCICActionStore
|
|
for action_store in self.action_options.action_store:
|
|
if action_store.store_id == store_id:
|
|
if (utils.object_exists_is_armature(action_store.object) or
|
|
utils.object_exists_is_light(action_store.object) or
|
|
utils.object_exists_is_camera(action_store.object)):
|
|
return action_store.object_action
|
|
return None
|
|
|
|
def fetch_action_stores(self, store_id: str):
|
|
"""This returns a list of the action store Property Collection items directly"""
|
|
action_stores = []
|
|
action_store: CCICActionStore
|
|
for action_store in self.action_options.action_store:
|
|
if action_store.store_id == store_id:
|
|
action_stores.append(action_store)
|
|
return action_stores
|
|
|
|
def fetch_stored_actions(self, store_id: str):
|
|
"""This returns a list of the action store Property Collection items directly"""
|
|
stored_actions = []
|
|
action_store: CCICActionStore
|
|
for action_store in self.action_options.action_store:
|
|
if action_store.store_id == store_id:
|
|
if action_store.object_action:
|
|
stored_actions.append(action_store.object_action)
|
|
if action_store.data_action:
|
|
stored_actions.append(action_store.data_action)
|
|
return stored_actions
|
|
|
|
def delete_action_store(self, store_id: str):
|
|
if store_id:
|
|
L = len(self.action_options.action_store)
|
|
for i in range(L-1, 0, -1):
|
|
action_store: CCICActionStore = self.action_options.action_store[i]
|
|
if action_store.store_id == store_id:
|
|
self.action_options.action_store.remove(i)
|
|
|
|
def get_action_store_for(self, store_id: str, obj):
|
|
action_store: CCICActionStore
|
|
if store_id:
|
|
for action_store in self.action_options.action_store:
|
|
if action_store.store_id == store_id:
|
|
if action_store.object == obj:
|
|
return action_store
|
|
return None
|
|
|
|
def restore_ui_list_indices(self):
|
|
"""Restore the indices from the stored objects, because adding new objects will cause the indices to become invalid."""
|
|
self.armature_list_index = utils.index_in_collection(self.armature_list_object, bpy.data.objects)
|
|
self.action_list_index = utils.index_in_collection(self.action_list_action, bpy.data.actions)
|
|
self.unity_action_list_index = utils.index_in_collection(self.unity_action_list_action, bpy.data.actions)
|
|
self.rigified_action_list_index = utils.index_in_collection(self.rigified_action_list_action, bpy.data.actions)
|
|
|
|
def store_ui_list_indices(self):
|
|
"""Store the indices as objects, because adding new objects will cause the indices to become invalid."""
|
|
self.armature_list_object = utils.collection_at_index(self.armature_list_index, bpy.data.objects)
|
|
self.action_list_action = utils.collection_at_index(self.action_list_index, bpy.data.actions)
|
|
self.action_set_list_action = utils.collection_at_index(self.action_set_list_index, bpy.data.actions)
|
|
self.unity_action_list_action = utils.collection_at_index(self.unity_action_list_index, bpy.data.actions)
|
|
self.rigified_action_list_action = utils.collection_at_index(self.rigified_action_list_index, bpy.data.actions)
|
|
if self.armature_list_object and self.armature_list_object.type != "ARMATURE":
|
|
self.armature_list_object = None
|
|
self.armature_list_index = -1
|
|
|
|
def hair_dir_vectors(self):
|
|
dirs = { "VERTICAL": self.hair_card_vertical_dir,
|
|
"HORIZONTAL": self.hair_card_horizontal_dir,
|
|
"SQUARE": self.hair_card_square_dir }
|
|
dir_vectors = {}
|
|
for aspect in dirs:
|
|
dir = dirs[aspect]
|
|
vector = Vector((0,0))
|
|
if dir == "UP":
|
|
vector = Vector((0,1)).normalized()
|
|
elif dir == "LEFT":
|
|
vector = Vector((-1,0)).normalized()
|
|
elif dir == "RIGHT":
|
|
vector = Vector((1,0)).normalized()
|
|
else: #if dir == "DOWN":
|
|
vector = Vector((0,-1)).normalized()
|
|
dir_vectors[aspect] = vector
|
|
return dir_vectors
|
|
|
|
def validate(self, report=None):
|
|
validation = True
|
|
chr_cache: CC3CharacterCache
|
|
for chr_cache in self.import_cache:
|
|
if not chr_cache.validate(report):
|
|
chr_cache.invalidate()
|
|
validation = False
|
|
return validation
|
|
|
|
def clean_up(self):
|
|
chr_cache: CC3CharacterCache
|
|
for chr_cache in self.import_cache:
|
|
chr_cache.clean_up()
|
|
clean_collection_property(self.import_cache)
|
|
|
|
def validate_and_clean_up(self):
|
|
self.validate()
|
|
self.clean_up()
|
|
# endregion
|
|
|
|
# region BakeProps
|
|
class CCICBakeCache(bpy.types.PropertyGroup):
|
|
uid: bpy.props.IntProperty(default=0)
|
|
object: bpy.props.PointerProperty(type=bpy.types.Object)
|
|
source_material: bpy.props.PointerProperty(type=bpy.types.Material)
|
|
baked_material: bpy.props.PointerProperty(type=bpy.types.Material)
|
|
|
|
|
|
class CCICBakeMaterialSettings(bpy.types.PropertyGroup):
|
|
material: bpy.props.PointerProperty(type=bpy.types.Material)
|
|
max_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="4096")
|
|
diffuse_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
ao_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
sss_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
transmission_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
thickness_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
metallic_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
specular_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
roughness_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
emissive_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
alpha_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
normal_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="4096")
|
|
micronormal_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
micronormalmask_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
bump_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
mask_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
detail_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
|
|
|
|
class CCICBakeProps(bpy.types.PropertyGroup):
|
|
auto_increment: bpy.props.IntProperty(default=100)
|
|
jpeg_quality: bpy.props.IntProperty(default=90, min=0, max=100)
|
|
png_compression: bpy.props.IntProperty(default=15, min=0, max=100)
|
|
|
|
target_mode: bpy.props.EnumProperty(items=vars.BAKE_TARGETS, default="BLENDER")
|
|
|
|
target_format: bpy.props.EnumProperty(items=vars.TARGET_FORMATS, default="JPEG")
|
|
|
|
bake_samples: bpy.props.IntProperty(default=5, min=1, max=64, description="The number of texture samples per pixel to bake. As there are no ray traced effects involved, 1 to 5 samples is usually enough.")
|
|
ao_in_diffuse: bpy.props.FloatProperty(default=0, min=0, max=1, description="How much of the ambient occlusion to bake into the diffuse")
|
|
|
|
smoothness_mapping: bpy.props.EnumProperty(items=vars.CONVERSION_FUNCTIONS, default="IR", description="Roughness to smoothness calculation")
|
|
|
|
allow_bump_maps: bpy.props.BoolProperty(default=True, description="Allow separate Bump and Normal Maps")
|
|
scale_maps: bpy.props.BoolProperty(default=False)
|
|
pack_gltf: bpy.props.BoolProperty(default=True, description="Pack AO, Roughness and Metallic into a single Texture for GLTF")
|
|
|
|
custom_sizes: bpy.props.BoolProperty(default=False)
|
|
bake_mixers: bpy.props.BoolProperty(default=True, description="Bake the result of any Color ID/RGB mask mixers on the materials")
|
|
max_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="4096")
|
|
diffuse_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
ao_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
sss_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
transmission_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
thickness_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
metallic_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
specular_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
roughness_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
emissive_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
alpha_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
normal_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="4096")
|
|
micronormal_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
micronormalmask_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="1024")
|
|
bump_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
mask_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
detail_size: bpy.props.EnumProperty(items=vars.TEX_LIST, default="2048")
|
|
|
|
bake_path: bpy.props.StringProperty(default="Bake", subtype="DIR_PATH")
|
|
material_settings: bpy.props.CollectionProperty(type=CCICBakeMaterialSettings)
|
|
bake_cache: bpy.props.CollectionProperty(type=CCICBakeCache)
|
|
# endregion
|
|
|
|
# region LinkProps
|
|
class CCICLinkProps(bpy.types.PropertyGroup):
|
|
# Data link props
|
|
link_status: bpy.props.StringProperty(default="")
|
|
remote_app: bpy.props.StringProperty(default="")
|
|
remote_version: bpy.props.StringProperty(default="")
|
|
remote_path: bpy.props.StringProperty(default="", subtype="DIR_PATH")
|
|
remote_exe: bpy.props.StringProperty(default="")
|
|
connected: bpy.props.BoolProperty(default=False)
|
|
reconnect: bpy.props.BoolProperty(default=False)
|
|
temp_folder: bpy.props.StringProperty(default="", subtype="DIR_PATH")
|
|
temp_files: bpy.props.StringProperty(default="", subtype="DIR_PATH")
|
|
# endregion |