# Copyright (C) 2021 Victor Soupday # This file is part of CC/iC 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 . 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 = node.inputs["Strength"].default_value 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 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) 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 "" 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","Add New","Import actions as a new set of actions and keep the existing actions"), ("REPLACE","Replace","Import new actions to replace the existing actions"), ("MIX","Mix","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="START", 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") 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) # some masking settings ... # some masking presets ... # export / import presets ... def test(self): self.import_mix_bones 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_height_scale: bpy.props.FloatProperty(default=0.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_height_scale")) skin_bump_scale: bpy.props.FloatProperty(default=0.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_bump_scale")) skin_height_delta_scale: bpy.props.FloatProperty(default=1.0, min=0.0, max=5.0, update=lambda s,c: update_property(s,c,"skin_height_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")) 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_height_scale: bpy.props.FloatProperty(default=0.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_height_scale")) skin_bump_scale: bpy.props.FloatProperty(default=0.0, min=0, max=2, update=lambda s,c: update_property(s,c,"skin_bump_scale")) 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")) 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")) 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")) 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_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")) 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")) 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_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")) 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, 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_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")) 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_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")) 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 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) 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)) class CCICActionStore(bpy.types.PropertyGroup): object: bpy.props.PointerProperty(type=bpy.types.Object) action: bpy.props.PointerProperty(type=bpy.types.Action) 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() 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.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) 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: bpy.props.BoolProperty(default=True, name="Filter", description="Show only motion sets compatible with the current character") # Hair hair_export_group_by: bpy.props.EnumProperty(items=[ ("CURVE","Curve","Group by curve objects"), ("NAME","Name","Gropu 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) 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 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_of_collection(self.armature_list_object, bpy.data.objects) self.action_list_index = utils.index_of_collection(self.action_list_action, bpy.data.actions) self.unity_action_list_index = utils.index_of_collection(self.unity_action_list_action, bpy.data.actions) self.rigified_action_list_index = utils.index_of_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() 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) 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")