diff --git a/.gitignore b/.gitignore index 36133cb6..b0a643ed 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ __pycache__ Runblender.bat.lnk .idea/inspectionProfiles/Project_Default.xml /mb-lab_updater/MB-Lab_updater_status.json +/.vscode/settings.json +/.vscode/launch.json diff --git a/CHANGELOG.md b/CHANGELOG.md index b3a91673..c1b22993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All changes will be documented here +# MB-Lab 1.7.4 + +## Added + +- New Procedural Eye shaders +- New Texture Mask for freckles + +## Changed + +- Deleted Principled BSDF shader networks for custom surface shaders +- Minor GUI edits +- Edited Bump and Albedo texture maps (NOT YET) +- Changed scaling of sub dermal map +- Updated Material Engine code for texture masks +- Added bug warning to Muscle checkbox +- When transferring weights for proxying, check the vertex is in the group +- Changed lighting setup using Area lights + +## Bug Fixes + +- Set lighting setup default to False, fixing a minor startup bug +- + # MB-Lab 1.7.3 ## Added diff --git a/__init__.py b/__init__.py index 34708339..d4dd9664 100644 --- a/__init__.py +++ b/__init__.py @@ -42,8 +42,8 @@ bl_info = { "name": "MB-Lab", - "author": "Manuel Bastioni", - "version": (1, 7, 3), + "author": "Manuel Bastioni, MB-Lab Community", + "version": (1, 7, 4), "blender": (2, 80, 0), "location": "View3D > Tools > MB-Lab", "description": "A complete lab for character creation", @@ -118,10 +118,10 @@ def start_lab_session(): else: scn.render.engine = 'BLENDER_EEVEE' if scn.mblab_use_lamps: - algorithms.import_object_from_lib(lib_filepath, "Lamp_back_bottom") - algorithms.import_object_from_lib(lib_filepath, "Lamp_back_up") - algorithms.import_object_from_lib(lib_filepath, "Lamp_left") - algorithms.import_object_from_lib(lib_filepath, "Lamp_right") + #algorithms.import_object_from_lib(lib_filepath, "Lamp_back_bottom") + algorithms.import_object_from_lib(lib_filepath, "Light_Key") + algorithms.import_object_from_lib(lib_filepath, "Light_Fill") + algorithms.import_object_from_lib(lib_filepath, "Light_Backlight") # algorithms.append_object_from_library(lib_filepath, [], "Lamp_") else: scn.render.engine = 'BLENDER_WORKBENCH' @@ -560,28 +560,28 @@ def load_proxy_item(self, context): bpy.types.Scene.mblab_use_muscle = bpy.props.BoolProperty( name="Use basic muscles", default=False, - description="Use basic muscle armature") + description="Use basic muscle armature. THIS IS BUGGY!") bpy.types.Scene.mblab_remove_all_modifiers = bpy.props.BoolProperty( name="Remove modifiers", default=False, - description="If checked, all the modifiers will be removed, except the armature one (displacement, subdivision, corrective smooth, etc) will be removed from the finalized character)") + description="If checked, all the modifiers will be removed, except the armature (displacement, subdivision, corrective smooth, etc)") bpy.types.Scene.mblab_use_cycles = bpy.props.BoolProperty( - name="Use Cycles materials (needed for skin shaders)", + name="Use Cycles materials", default=True, update=set_cycles_render_engine, description="This is needed in order to use the skin editor and shaders (highly recommended)") bpy.types.Scene.mblab_use_eevee = bpy.props.BoolProperty( - name="Use EEVEE materials (needed for skin shaders)", + name="Use EEVEE materials", default=False, update=set_eevee_render_engine, description="This is needed in order to use the skin editor and shaders") bpy.types.Scene.mblab_use_lamps = bpy.props.BoolProperty( - name="Use portrait studio lights (recommended)", - default=True, + name="Use portrait studio lights", + default=False, description="Add a set of lights optimized for portrait. Useful during the design of skin (recommended)") bpy.types.Scene.mblab_show_measures = bpy.props.BoolProperty( @@ -665,7 +665,7 @@ def load_proxy_item(self, context): bpy.types.Scene.mblab_preserve_fantasy = bpy.props.BoolProperty( name="Fantasy", - description="Preserve the current amount of fantasy morphs. For example, starting from a character with zero fantasy elements, all the generated characters will have zero fantasy elements") + description="Preserve the current amount of fantasy morphs") bpy.types.Scene.mblab_preserve_body = bpy.props.BoolProperty( name="Body", @@ -1233,10 +1233,7 @@ class FinalizeCharacterAndImages(bpy.types.Operator, ExportHelper): bl_label = 'Finalize with textures and backup' bl_idname = 'mbast.finalize_character_and_images' filename_ext = ".png" - filter_glob: bpy.props.StringProperty( - default="*.png", - options={'HIDDEN'}, - ) + filter_glob: bpy.props.StringProperty(default="*.png", options={'HIDDEN'},) bl_description = 'Finalize, saving all the textures and converting the parameters in shapekeys. Warning: after the conversion the character will be no longer modifiable using MB-Lab tools' bl_context = 'objectmode' bl_options = {'REGISTER', 'INTERNAL'} @@ -1832,75 +1829,43 @@ def draw(self, context): scn = bpy.context.scene icon_expand = "DISCLOSURE_TRI_RIGHT" icon_collapse = "DISCLOSURE_TRI_DOWN" + + box = self.layout.box() + box.label(text="https://github.com/animate1978/MB-Lab") if gui_status == "ERROR_SESSION": box = self.layout.box() box.label(text=gui_err_msg, icon="INFO") if gui_status == "NEW_SESSION": - # box = self.layout.box() - - self.layout.label(text="https://github.com/animate1978/MB-Lab") + self.layout.label(text="CREATION TOOLS") - self.layout.prop(scn, 'mblab_character_name') - + box = self.layout.box() + box.prop(scn, 'mblab_character_name') + if mblab_humanoid.is_ik_rig_available(scn.mblab_character_name): - self.layout.prop(scn, 'mblab_use_ik') + box.prop(scn, 'mblab_use_ik') if mblab_humanoid.is_muscle_rig_available(scn.mblab_character_name): - self.layout.prop(scn, 'mblab_use_muscle') + box.prop(scn, 'mblab_use_muscle') - self.layout.prop(scn, 'mblab_use_cycles') - self.layout.prop(scn, 'mblab_use_eevee') + box.prop(scn, 'mblab_use_cycles') + box.prop(scn, 'mblab_use_eevee') if scn.mblab_use_cycles or scn.mblab_use_eevee: - self.layout.prop(scn, 'mblab_use_lamps') - self.layout.operator('mbast.init_character') + box.prop(scn, 'mblab_use_lamps') + box.operator('mbast.init_character', icon='ARMATURE_DATA') if gui_status != "ACTIVE_SESSION": self.layout.label(text=" ") self.layout.label(text="AFTER-CREATION TOOLS") # face rig button - self.layout.operator('mbast.create_face_rig') - self.layout.operator('mbast.delete_face_rig') - - if gui_active_panel_fin != "assets": - self.layout.operator('mbast.button_assets_on', icon=icon_expand) - else: - self.layout.operator('mbast.button_assets_off', icon=icon_collapse) - # assets_status = mblab_proxy.validate_assets_fitting() - box = self.layout.box() - - box.prop(scn, 'mblab_proxy_library') - box.prop(scn, 'mblab_assets_models') - # box.operator('mbast.load_assets_element') - box.label(text="To adapt the asset, use the proxy fitting tool", icon='INFO') - - if gui_active_panel_fin != "pose": - self.layout.operator('mbast.button_pose_on', icon=icon_expand) - else: - self.layout.operator('mbast.button_pose_off', icon=icon_collapse) - box = self.layout.box() - - armature = utils.get_active_armature() - if armature is not None and not utils.is_ik_armature(armature): - box.enabled = True - sel_gender = algorithms.get_selected_gender() - if sel_gender == "FEMALE": - if mblab_retarget.femaleposes_exist: - box.prop(armature, "female_pose") - if sel_gender == "MALE": - if mblab_retarget.maleposes_exist: - box.prop(armature, "male_pose") - box.operator("mbast.pose_load", icon='IMPORT') - box.operator("mbast.pose_save", icon='EXPORT') - box.operator("mbast.pose_reset", icon='ARMATURE_DATA') - box.operator("mbast.load_animation", icon='IMPORT') - else: - box.enabled = False - box.label(text="Please select the lab character (IK not supported)", icon='INFO') + box = self.layout.box() + box.label(text="Face Rig") + box.operator('mbast.create_face_rig') + box.operator('mbast.delete_face_rig') if gui_active_panel_fin != "expressions": - self.layout.operator('mbast.button_expressions_on', icon=icon_expand) + box.operator('mbast.button_expressions_on', icon=icon_expand) else: self.layout.operator('mbast.button_expressions_off', icon=icon_collapse) box = self.layout.box() @@ -1919,11 +1884,24 @@ def draw(self, context): else: box.enabled = False box.label(text="No express. shapekeys", icon='INFO') + + if gui_active_panel_fin != "assets": + box.operator('mbast.button_assets_on', icon=icon_expand) + else: + box.operator('mbast.button_assets_off', icon=icon_collapse) + # assets_status = mblab_proxy.validate_assets_fitting() + box = self.layout.box() + box.prop(scn, 'mblab_proxy_library') + box.prop(scn, 'mblab_assets_models') + # box.operator('mbast.load_assets_element') + box.label(text="To adapt the asset, use the proxy fitting tool", icon='INFO') + + if gui_active_panel_fin != "proxy_fit": - self.layout.operator('mbast.button_proxy_fit_on', icon=icon_expand) + box.operator('mbast.button_proxy_fit_on', icon=icon_expand) else: - self.layout.operator('mbast.button_proxy_fit_off', icon=icon_collapse) + box.operator('mbast.button_proxy_fit_off', icon=icon_collapse) fitting_status, proxy_obj, reference_obj = mblab_proxy.get_proxy_fitting_ingredients() box = self.layout.box() @@ -1966,9 +1944,33 @@ def draw(self, context): if fitting_status == 'NO_MESH_SELECTED': box.enabled = False box.label(text="Selected proxy is not a mesh", icon="INFO") + + if gui_active_panel_fin != "pose": + box.operator('mbast.button_pose_on', icon=icon_expand) + else: + self.layout.operator('mbast.button_pose_off', icon=icon_collapse) + box = self.layout.box() + armature = utils.get_active_armature() + if armature is not None and not utils.is_ik_armature(armature): + box.enabled = True + sel_gender = algorithms.get_selected_gender() + if sel_gender == "FEMALE": + if mblab_retarget.femaleposes_exist: + box.prop(armature, "female_pose") + if sel_gender == "MALE": + if mblab_retarget.maleposes_exist: + box.prop(armature, "male_pose") + box.operator("mbast.pose_load", icon='IMPORT') + box.operator("mbast.pose_save", icon='EXPORT') + box.operator("mbast.pose_reset", icon='ARMATURE_DATA') + box.operator("mbast.load_animation", icon='IMPORT') + else: + box.enabled = False + box.label(text="Please select the lab character (IK not supported)", icon='INFO') + if gui_active_panel_fin != "utilities": - self.layout.operator('mbast.button_utilities_on', icon=icon_expand) + box.operator('mbast.button_utilities_on', icon=icon_expand) else: self.layout.operator('mbast.button_utilities_off', icon=icon_collapse) @@ -2003,7 +2005,7 @@ def draw(self, context): age_lbl = round((15.5 * x_age ** 2) + 31 * x_age + 33) mass_lbl = round(50 * (x_mass + 1)) tone_lbl = round(50 * (x_tone + 1)) - lbl_text = "Age: {0}y Mass: {1}% Tone: {2}% ".format(age_lbl, mass_lbl, tone_lbl) + lbl_text = "Age: {0} yr. Mass: {1}% Tone: {2}% ".format(age_lbl, mass_lbl, tone_lbl) self.layout.label(text=lbl_text, icon="RNA") for meta_data_prop in sorted(mblab_humanoid.character_metaproperties.keys()): if "last" not in meta_data_prop: diff --git a/data/characters_config.json b/data/characters_config.json index 76c71303..657c142d 100644 --- a/data/characters_config.json +++ b/data/characters_config.json @@ -70,6 +70,7 @@ "texture_bump": "human_female_bump.png", "texture_displacement": "human_female_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_female_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_female_morphs.json", "shared_morphs_extra_file": "human_female_morphs_extra.json", @@ -95,6 +96,7 @@ "texture_bump": "human_female_bump.png", "texture_displacement": "human_female_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_female_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_female_morphs.json", "shared_morphs_extra_file": "human_female_morphs_extra.json", @@ -120,6 +122,7 @@ "texture_bump": "human_female_bump.png", "texture_displacement": "human_female_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_female_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_female_morphs.json", "shared_morphs_extra_file": "human_female_morphs_extra.json", @@ -145,6 +148,7 @@ "texture_bump": "human_male_bump.png", "texture_displacement": "human_male_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_male_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_male_morphs.json", "shared_morphs_extra_file": "human_male_morphs_extra.json", @@ -170,6 +174,7 @@ "texture_bump": "human_male_bump.png", "texture_displacement": "human_male_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_male_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_male_morphs.json", "shared_morphs_extra_file": "human_male_morphs_extra.json", @@ -195,6 +200,7 @@ "texture_bump": "human_male_bump.png", "texture_displacement": "human_male_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_male_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_male_morphs.json", "shared_morphs_extra_file": "human_male_morphs_extra.json", @@ -220,6 +226,7 @@ "texture_bump": "", "texture_displacement": "", "texture_eyes": "anime_female_albedo.png", + "texture_frecklemask": "", "morphs_extra_file": "", "shared_morphs_file": "anime_female_morphs.json", "shared_morphs_extra_file": "", @@ -247,6 +254,7 @@ "texture_bump": "", "texture_displacement": "", "texture_eyes": "anime_female_albedo.png", + "texture_frecklemask": "", "morphs_extra_file": "", "shared_morphs_file": "anime_female_morphs.json", "shared_morphs_extra_file": "", @@ -274,6 +282,7 @@ "texture_bump": "human_female_bump.png", "texture_displacement": "human_female_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_female_frecklemask.png", "morphs_extra_file": "f_an03_morphs_extra.json", "shared_morphs_file": "human_female_morphs.json", "shared_morphs_extra_file": "human_female_morphs_extra.json", @@ -299,6 +308,7 @@ "texture_bump": "", "texture_displacement": "", "texture_eyes": "anime_male_albedo.png", + "texture_frecklemask": "", "morphs_extra_file": "", "shared_morphs_file": "anime_male_morphs.json", "shared_morphs_extra_file": "", @@ -326,6 +336,7 @@ "texture_bump": "", "texture_displacement": "", "texture_eyes": "anime_male_albedo.png", + "texture_frecklemask": "", "morphs_extra_file": "", "shared_morphs_file": "anime_male_morphs.json", "shared_morphs_extra_file": "", @@ -353,6 +364,7 @@ "texture_bump": "human_male_bump.png", "texture_displacement": "human_male_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_male_frecklemask.png", "morphs_extra_file": "m_an03_morphs_extra.json", "shared_morphs_file": "human_male_morphs.json", "shared_morphs_extra_file": "human_male_morphs_extra.json", @@ -378,6 +390,7 @@ "texture_bump": "human_female_bump.png", "texture_displacement": "human_female_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_female_frecklemask.png", "morphs_extra_file": "f_ft01_morphs_extra.json", "shared_morphs_file": "human_female_morphs.json", "shared_morphs_extra_file": "human_female_morphs_extra.json", @@ -403,6 +416,7 @@ "texture_bump": "human_male_bump.png", "texture_displacement": "human_male_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_male_frecklemask.png", "morphs_extra_file": "", "shared_morphs_file": "human_male_morphs.json", "shared_morphs_extra_file": "human_male_morphs_extra.json", @@ -428,6 +442,7 @@ "texture_bump": "human_male_bump.png", "texture_displacement": "human_male_displacement.png", "texture_eyes": "eyes_albedo.png", + "texture_frecklemask": "human_male_frecklemask.png", "morphs_extra_file": "m_ft02_morphs_extra.json", "shared_morphs_file": "human_male_morphs.json", "shared_morphs_extra_file": "human_male_morphs_extra.json", diff --git a/docs/new_features.rst b/docs/new_features.rst index 4e246d83..895fe63f 100644 --- a/docs/new_features.rst +++ b/docs/new_features.rst @@ -1,6 +1,22 @@ New Features ============ +Version 1.7.4 + +Shaders for this version have been re-worked from the ground up to try to achieve a realistic look as much as possible. + +The skin shader has dropped the Principled BSDF node in favor of a new custom node network. + +Procedural veins have been added. The visibility of the veins are controlled by a slider in the skin editor. + +The eyes have become completely procedural in this version, which allows a greater range of color for the iris than before. + +Eye vein visiblity is controlled with a slider. + +The lighting setup has been changed to a three point arrangment using Area lights now. + + + In version 1.7.3 is the addition of the Auto Updater. .. image:: images/auto_updater_173.png diff --git a/docs/updates.rst b/docs/updates.rst index c80c9887..27476be0 100644 --- a/docs/updates.rst +++ b/docs/updates.rst @@ -1,6 +1,22 @@ Updates ======= +============ +MB-Lab 1.7.4 +============ + +* New Procedural Eye shaders +* New Texture Mask for freckles +* Deleted Principled BSDF shader networks for custom surface shaders +* Minor GUI edits +* Edited Bump and Albedo texture maps +* Changed scaling of sub dermal map +* Updated Material Engine code for texture masks +* Added bug warning to Muscle checkbox +* When transferring weights for proxying, check the vertex is in the group +* Changed lighting setup using Area lights +* Set lighting setup default to False, fixing a minor startup bug + ============== MB-Lab 1.7.3 ============== diff --git a/materialengine.py b/materialengine.py index 3ae5bd45..2592b20a 100644 --- a/materialengine.py +++ b/materialengine.py @@ -53,6 +53,7 @@ def __init__(self, obj_name, character_config): "eyes_albedo": character_config["texture_eyes"], "body_bump": character_config["texture_bump"], "body_subd": character_config["texture_subdermal"], + "freckle_mask": character_config["texture_frecklemask"], } image_file_paths = {} @@ -104,6 +105,10 @@ def texture_bump_exist(self): @property def texture_displace_exist(self): return os.path.isfile(self.image_file_paths["displ_data"]) + + @property + def texture_frecklemask_exist(self): + return os.path.isfile(self.image_file_paths["freckle_mask"]) @staticmethod def calculate_disp_pixels(blender_image, age_factor, tone_factor, mass_factor): @@ -175,13 +180,11 @@ def update_shaders(self, material_parameters=[], update_textures_nodes=True): if "_skn_specular" in node.name: self.assign_image_to_node(material.name, node.name, self.image_file_names["body_spec"]) if "_skn_roughness" in node.name: - self.assign_image_to_node(material.name, node.name, - self.image_file_names["body_rough"]) + self.assign_image_to_node(material.name, node.name, self.image_file_names["body_rough"]) if "_skn_subdermal" in node.name: self.assign_image_to_node(material.name, node.name, self.image_file_names["body_subd"]) if "_eys_albedo" in node.name: - self.assign_image_to_node(material.name, node.name, - self.image_file_names["eyes_albedo"]) + self.assign_image_to_node(material.name, node.name, self.image_file_names["eyes_albedo"]) if "_eylsh_albedo" in node.name: self.assign_image_to_node(material.name, node.name, self.image_file_names["body_derm"]) if "_tth_albedo" in node.name: @@ -189,8 +192,9 @@ def update_shaders(self, material_parameters=[], update_textures_nodes=True): if "_skn_bump" in node.name: self.assign_image_to_node(material.name, node.name, self.image_file_names["body_bump"]) if "_skn_disp" in node.name: - self.assign_image_to_node(material.name, node.name, - self.image_file_names["body_displ"]) + self.assign_image_to_node(material.name, node.name, self.image_file_names["body_displ"]) + if "_skn_frecklemask" in node.name: + self.assign_image_to_node(material.name, node.name, self.image_file_names["freckle_mask"]) def rename_skin_shaders(self, prefix): obj = self.get_object() diff --git a/proxyengine.py b/proxyengine.py index b1327768..17712c90 100644 --- a/proxyengine.py +++ b/proxyengine.py @@ -79,12 +79,10 @@ def transfer_weights(self, body, proxy): body_verts_weights = [[] for v in body.data.vertices] - for grp in body.vertex_groups: + for gid, grp in enumerate(body.vertex_groups): for idx, w_data in enumerate(body_verts_weights): - try: - w_data.append([grp.name,grp.weight(idx)]) - except: - pass #TODO: idx in grp.weight + in_group = gid in [g.group for g in body.data.vertices[idx].groups] + w_data.append([grp.name,grp.weight(idx) if in_group else 0]) for p_idx, proxy_vert in enumerate(proxy_vertices):