diff --git a/README.md b/README.md index 44000ea3..9bac64e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Cats Blender Plugin (0.12.0) +# Cats Blender Plugin (0.12.1) A tool designed to shorten steps needed to import and optimize models into VRChat. Compatible models are: MMD, XNALara, Mixamo, Source Engine, Unreal Engine, DAZ/Poser, Blender Rigify, Sims 2, Motion Builder, 3DS Max and potentially more @@ -333,6 +333,13 @@ It checks for a new version automatically once every day. ## Changelog +#### 0.12.1 +- **General**: + - Fixed an error removing the whole Cats UI + - Fixed an error on Fix Model and other various buttons + - Stopping Pose Mode in Blender 2.8 now selects the Box Selection tool instead of the 3D Cursor + - Updated mmd_tools (improved 2.8 materials) + #### 0.12.0 - **Model**: - Made VRoid (.vrm) models compatible @@ -390,29 +397,6 @@ It checks for a new version automatically once every day. - **Custom Model Creation**: - Fixed Attach Mesh throwing an error when the mesh has no vertex groups -#### 0.11.3 [YANKED] - -#### 0.11.2 -- **Model**: - - Fix: Joints and Rigidbodies now get deleted correctly - -#### 0.11.1 -- **Export**: - - Embed Textures is not longer enabled by default, but can now be enabled in the settings -- **Settings and Updates:** - - Renamed "Updater" panel to "Settings & Updates" - - Added setting to embed textures on export - - Settings are saved into a local settings file - - Issue: Settings get reset with every CATS update currently - - This panel now shows the current CATS version -- **Translations:** - - Added a warning when you are temporarily IP banned by Google Translate -- **Eye Tracking:** - - Wink Right shape keys are now detected more reliably -- **General**: - - Cats no longer unhides everything whenever it does something, it only unhides the current model - - This helps a lot when working with multiple models - Read the full changelog [here](https://github.com/michaeldegroot/cats-blender-plugin/releases). diff --git a/__init__.py b/__init__.py index 2e94517a..6194f632 100644 --- a/__init__.py +++ b/__init__.py @@ -30,7 +30,7 @@ 'author': 'GiveMeAllYourCats', 'location': 'View 3D > Tool Shelf > CATS', 'description': 'A tool designed to shorten steps needed to import and optimize models into VRChat', - 'version': [0, 12, 0], # Only change this version and the dev branch var right before publishing the new update! + 'version': (0, 12, 1), # Has to be (x, x, x) not [x, x, x]!! # Only change this version and the dev branch var right before publishing the new update! 'blender': (2, 80, 0), 'wiki_url': 'https://github.com/michaeldegroot/cats-blender-plugin', 'tracker_url': 'https://github.com/michaeldegroot/cats-blender-plugin/issues', @@ -117,8 +117,8 @@ def register(): # Register updater try: addon_updater_ops.register(bl_info) - except ValueError: - print('Error while registering updater.') + except ValueError as e: + print('\n!!! Error while registering Updater:\n' + str(e) + '\n') pass # Register all classes diff --git a/mmd_tools_local/core/material.py b/mmd_tools_local/core/material.py index d69f762c..f6717b10 100644 --- a/mmd_tools_local/core/material.py +++ b/mmd_tools_local/core/material.py @@ -12,7 +12,7 @@ SPHERE_MODE_ADD = 2 SPHERE_MODE_SUBTEX = 3 -class FnMaterial(object): +class _FnMaterialBI: __BASE_TEX_SLOT = 0 __TOON_TEX_SLOT = 1 __SPHERE_TEX_SLOT = 2 @@ -342,153 +342,426 @@ def update_self_shadow(self): mat.use_transparent_shadows = mmd_mat.enabled_self_shadow def update_enabled_toon_edge(self): - mat = self.__material - if not hasattr(mat, 'line_color'): # freestyle line color - return - mmd_mat = mat.mmd_material - mat.line_color[3] = min(int(mmd_mat.enabled_toon_edge), mmd_mat.edge_color[3]) + self.update_edge_color() def update_edge_color(self): mat = self.__material - if not hasattr(mat, 'line_color'): # freestyle line color - return mmd_mat = mat.mmd_material - r, g, b, a = mmd_mat.edge_color - mat.line_color = [r, g, b, min(int(mmd_mat.enabled_toon_edge), a)] + color, alpha = mmd_mat.edge_color[:3], mmd_mat.edge_color[3] + line_color = color + (min(alpha, int(mmd_mat.enabled_toon_edge)),) + if hasattr(mat, 'line_color'): # freestyle line color + mat.line_color = line_color + + mat_edge = bpy.data.materials.get('mmd_edge.'+mat.name, None) + if mat_edge: + mat_edge.mmd_material.edge_color = line_color + + if mat.name.startswith('mmd_edge.') and mat.node_tree: + mmd_mat.ambient_color, mmd_mat.alpha = color, alpha + node_shader = mat.node_tree.nodes.get('mmd_edge_preview', None) + if node_shader and 'Color' in node_shader.inputs: + node_shader.inputs['Color'].default_value = mmd_mat.edge_color + if node_shader and 'Alpha' in node_shader.inputs: + node_shader.inputs['Alpha'].default_value = alpha def update_edge_weight(self): pass -if bpy.app.version >= (2, 80, 0): - from bpy_extras.node_shader_utils import PrincipledBSDFWrapper - - class _DummyTexture: - def __init__(self, image): - self.type = 'IMAGE' - self.image = image - self.use_mipmap = True - - class _DummyTextureSlot: - def __init__(self, image): - self.diffuse_color_factor = 1 - self.uv_layer = '' - self.texture = _DummyTexture(image) - - __FnMaterialBase = FnMaterial - class FnMaterial(__FnMaterialBase): - @property - def __shader_wrap(self): - if not hasattr(self, '_shader_wrapper'): - self._shader_wrapper = PrincipledBSDFWrapper(self.material, is_readonly=True) - return self._shader_wrapper - - @property - def __shader_read(self): - shader = self.__shader_wrap - shader.is_readonly = True - return shader +class _DummyTexture: + def __init__(self, image): + self.type = 'IMAGE' + self.image = image + self.use_mipmap = True - @property - def __shader(self): - shader = self.__shader_wrap - shader.is_readonly = False - shader.use_nodes = True - return shader +class _DummyTextureSlot: + def __init__(self, image): + self.diffuse_color_factor = 1 + self.uv_layer = '' + self.texture = _DummyTexture(image) - def get_texture(self): - texture = self.__shader_read.base_color_texture - if texture and texture.image: - return _DummyTexture(texture.image) - return None +class _FnMaterialCycles(_FnMaterialBI): + def get_texture(self): + return self.__get_texture_node('mmd_base_tex', use_dummy=True) - def create_texture(self, filepath): - image = self._load_image(filepath) - shader = self.__shader - shader.base_color_texture.image = image - return _DummyTextureSlot(image) + def create_texture(self, filepath): + texture = self.__create_texture_node('mmd_base_tex', filepath, (-4, -1)) + return _DummyTextureSlot(texture.image) - def remove_texture(self): - shader = self.__shader - shader.base_color_texture.image = None + def remove_texture(self): + self.__remove_texture_node('mmd_base_tex') - def get_sphere_texture(self): - texture = self.__shader_read.metallic_texture - if texture and texture.image: - return _DummyTexture(texture.image) - return None + def get_sphere_texture(self): + return self.__get_texture_node('mmd_sphere_tex', use_dummy=True) - def use_sphere_texture(self, use_sphere, obj=None): - pass + def use_sphere_texture(self, use_sphere, obj=None): + if use_sphere: + self.update_sphere_texture_type(obj) + else: + self.__update_shader_input('Sphere Tex Fac', 0) - def create_sphere_texture(self, filepath, obj=None): - image = self._load_image(filepath) - shader = self.__shader - shader.metallic_texture.image = image - shader.metallic_texture.texcoords = 'Normal' - shader.metallic_texture.node_mapping.vector_type = 'POINT' - shader.metallic_texture.node_mapping.translation = (0.5, 0.5, 0.0) - shader.metallic_texture.node_mapping.scale = (0.5, 0.5, 1.0) - return _DummyTextureSlot(image) + def create_sphere_texture(self, filepath, obj=None): + texture = self.__create_texture_node('mmd_sphere_tex', filepath, (-2, -2)) + sphere_texture_type = int(self.material.mmd_material.sphere_texture_type) + texture.color_space = 'NONE' if sphere_texture_type == 2 else 'COLOR' + self.update_sphere_texture_type(obj) + return _DummyTextureSlot(texture.image) - def update_sphere_texture_type(self, obj=None): - pass + def update_sphere_texture_type(self, obj=None): + sphere_texture_type = int(self.material.mmd_material.sphere_texture_type) + if sphere_texture_type not in (1, 2, 3): + self.__update_shader_input('Sphere Tex Fac', 0) + else: + self.__update_shader_input('Sphere Tex Fac', 1) + self.__update_shader_input('Sphere Mul/Add', sphere_texture_type == 2) + self.__update_shader_input('Sphere Tex', (0, 0, 0, 1) if sphere_texture_type == 2 else (1, 1, 1, 1)) + + texture = self.__get_texture_node('mmd_sphere_tex') + if texture: + mat = self.material + nodes, links = mat.node_tree.nodes, mat.node_tree.links + if sphere_texture_type == 3: + if obj and obj.type == 'MESH' and mat in tuple(obj.data.materials): + uv_layers = (l for l in obj.data.uv_layers if not l.name.startswith('_')) + next(uv_layers, None) # skip base UV + subtex_uv = getattr(next(uv_layers, None), 'name', '') + if subtex_uv != 'UV1': + print(' * material(%s): object "%s" use UV "%s" for SubTex'%(mat.name, obj.name, subtex_uv)) + links.new(nodes['mmd_tex_uv'].outputs['SubTex UV'], texture.inputs['Vector']) + else: + links.new(nodes['mmd_tex_uv'].outputs['Sphere UV'], texture.inputs['Vector']) - def remove_sphere_texture(self): - shader = self.__shader - shader.metallic_texture.image = None + def remove_sphere_texture(self): + self.__remove_texture_node('mmd_sphere_tex') - def get_toon_texture(self): - return None + def get_toon_texture(self): + return self.__get_texture_node('mmd_toon_tex', use_dummy=True) - def use_toon_texture(self, use_toon): - pass + def use_toon_texture(self, use_toon): + self.__update_shader_input('Toon Tex Fac', use_toon) - def create_toon_texture(self, filepath): - image = self._load_image(filepath) - return _DummyTextureSlot(image) + def create_toon_texture(self, filepath): + texture = self.__create_texture_node('mmd_toon_tex', filepath, (-3, -1.5)) + return _DummyTextureSlot(texture.image) - def remove_toon_texture(self): - pass + def remove_toon_texture(self): + self.__remove_texture_node('mmd_toon_tex') - def update_diffuse_color(self): - mat = self.__material - mmd_mat = mat.mmd_material - shader = self.__shader - shader.base_color = self._mixDiffuseAndAmbient(mmd_mat) + def __get_texture_node(self, node_name, use_dummy=False): + mat = self.material + texture = getattr(mat.node_tree, 'nodes', {}).get(node_name, None) + if isinstance(texture, bpy.types.ShaderNodeTexImage): + return _DummyTexture(texture.image) if use_dummy else texture + return None - def update_alpha(self): - mat = self.__material - mmd_mat = mat.mmd_material - shader = self.__shader - shader.transmission = 1 - mmd_mat.alpha - shader.ior = 1 - mat.blend_method = 'BLEND' + def __remove_texture_node(self, node_name): + mat = self.material + texture = getattr(mat.node_tree, 'nodes', {}).get(node_name, None) + if isinstance(texture, bpy.types.ShaderNodeTexImage): + mat.node_tree.nodes.remove(texture) + mat.update_tag() + + def __create_texture_node(self, node_name, filepath, pos): + texture = self.__get_texture_node(node_name) + if texture is None: + from mathutils import Vector + self.__update_shader_nodes() + nodes = self.material.node_tree.nodes + texture = nodes.new('ShaderNodeTexImage') + texture.label = bpy.path.display_name(node_name) + texture.name = node_name + texture.location = nodes['mmd_shader'].location + Vector((pos[0]*210, pos[1]*220)) + texture.image = self._load_image(filepath) + self.__update_shader_nodes() + return texture - def update_specular_color(self): - mat = self.__material - mmd_mat = mat.mmd_material - mat.specular_color = mmd_mat.specular_color - shader = self.__shader - shader.specular = mmd_mat.specular_color.v - def update_shininess(self): - mat = self.__material - mmd_mat = mat.mmd_material - shader = self.__shader - shader.roughness = 1/max(mmd_mat.shininess, 1) + def update_ambient_color(self): + mat = self.material + mmd_mat = mat.mmd_material + mat.diffuse_color = self._mixDiffuseAndAmbient(mmd_mat) + self.__update_shader_input('Ambient Color', mmd_mat.ambient_color[:]+(1,)) - def update_is_double_sided(self): - mat = self.__material - mmd_mat = mat.mmd_material + def update_diffuse_color(self): + mat = self.material + mmd_mat = mat.mmd_material + mat.diffuse_color = self._mixDiffuseAndAmbient(mmd_mat) + self.__update_shader_input('Diffuse Color', mmd_mat.diffuse_color[:]+(1,)) + + def update_alpha(self): + mat = self.material + mmd_mat = mat.mmd_material + if hasattr(mat, 'blend_method'): + mat.blend_method = 'HASHED' # 'BLEND' + #mat.show_transparent_backside = False + elif hasattr(mat, 'transparency_method'): + mat.use_transparency = True + mat.transparency_method = 'Z_TRANSPARENCY' + mat.game_settings.alpha_blend = 'ALPHA' + if hasattr(mat, 'alpha'): + mat.alpha = mmd_mat.alpha + self.__update_shader_input('Alpha', mmd_mat.alpha) + + def update_specular_color(self): + mat = self.material + mmd_mat = mat.mmd_material + mat.specular_color = mmd_mat.specular_color + self.__update_shader_input('Specular Color', mmd_mat.specular_color[:]+(1,)) + + def update_shininess(self): + mat = self.material + mmd_mat = mat.mmd_material + mat.roughness = 1/max(mmd_mat.shininess, 1) + if hasattr(mat, 'matallic'): + mat.matallic = 1 - mat.roughness + if hasattr(mat, 'specular_hardness'): + mat.specular_hardness = mmd_mat.shininess + self.__update_shader_input('Reflect', mmd_mat.shininess) + + def update_is_double_sided(self): + mat = self.material + mmd_mat = mat.mmd_material + if hasattr(mat, 'game_settings'): + mat.game_settings.use_backface_culling = not mmd_mat.is_double_sided + self.__update_shader_input('Double Sided', mmd_mat.is_double_sided) + + def update_self_shadow_map(self): + mat = self.material + mmd_mat = mat.mmd_material + cast_shadows = mmd_mat.enabled_self_shadow_map if mmd_mat.alpha > 1e-3 else False + if hasattr(mat, 'transparent_shadow_method'): + mat.transparent_shadow_method = 'HASHED' if cast_shadows else 'NONE' + + def update_self_shadow(self): + mat = self.material + mmd_mat = mat.mmd_material + self.__update_shader_input('Self Shadow', mmd_mat.enabled_self_shadow) - def update_self_shadow_map(self): - pass - def update_self_shadow(self): - mat = self.__material + def __update_shader_input(self, name, val): + mat = self.material + if mat.name.startswith('mmd_'): # skip mmd_edge.* + return + self.__update_shader_nodes() + shader = mat.node_tree.nodes.get('mmd_shader', None) + if shader and name in shader.inputs: + shader.inputs[name].default_value = val + + def __update_shader_nodes(self): + mat = self.material + if mat.node_tree is None: + mat.use_nodes = True + mat.node_tree.nodes.clear() + + from mathutils import Vector + nodes, links = mat.node_tree.nodes, mat.node_tree.links + + node_shader = nodes.get('mmd_shader', None) + if node_shader is None: + node_shader = nodes.new('ShaderNodeGroup') + node_shader.name = 'mmd_shader' + node_shader.location = (0, 1500) + node_shader.width = 200 + node_shader.node_tree = self.__get_shader() + mmd_mat = mat.mmd_material + node_shader.inputs['Ambient Color'].default_value = mmd_mat.ambient_color[:] + (1,) + node_shader.inputs['Diffuse Color'].default_value = mmd_mat.diffuse_color[:] + (1,) + node_shader.inputs['Specular Color'].default_value = mmd_mat.specular_color[:] + (1,) + node_shader.inputs['Alpha'].default_value = mmd_mat.alpha + node_shader.inputs['Reflect'].default_value = mmd_mat.shininess + node_shader.inputs['Double Sided'].default_value = mmd_mat.is_double_sided + + node_uv = nodes.get('mmd_tex_uv', None) + if node_uv is None: + node_uv = nodes.new('ShaderNodeGroup') + node_uv.name = 'mmd_tex_uv' + node_uv.location = node_shader.location + Vector((-5*210, -2.5*220)) + node_uv.node_tree = self.__get_shader_uv() + + if not node_shader.outputs['Shader'].is_linked: + node_output = next((n for n in nodes if isinstance(n, bpy.types.ShaderNodeOutputMaterial) and n.is_active_output), None) + if node_output is None: + node_output = nodes.new('ShaderNodeOutputMaterial') + node_output.location = node_shader.location + Vector((400, 0)) + node_output.is_active_output = True + links.new(node_shader.outputs['Shader'], node_output.inputs['Surface']) + + for name_id in ('Base', 'Toon', 'Sphere'): + texture = self.__get_texture_node('mmd_%s_tex'%name_id.lower()) + if texture: + if not texture.outputs['Color'].is_linked: + links.new(texture.outputs['Color'], node_shader.inputs[name_id+' Tex']) + if not texture.outputs['Alpha'].is_linked: + links.new(texture.outputs['Alpha'], node_shader.inputs[name_id+' Alpha']) + if not texture.inputs['Vector'].is_linked: + links.new(node_uv.outputs[name_id+' UV'], texture.inputs['Vector']) + + def __get_shader_uv(self): + group_name = 'MMDTexUV' + shader = bpy.data.node_groups.get(group_name, None) or bpy.data.node_groups.new(name=group_name, type='ShaderNodeTree') + if len(shader.nodes): + return shader + + nodes, links = shader.nodes, shader.links + + def __new_node(idname, pos): + node = nodes.new(idname) + node.location = (pos[0]*210, pos[1]*220) + return node + + def __new_io(shader_io, io_sockets, io_name, socket): + links.new(io_sockets[-1], socket) + shader_io[-1].name = io_name + + ############################################################################ + node_output = __new_node('NodeGroupOutput', (6, 0)) + + tex_coord = __new_node('ShaderNodeTexCoord', (0, 0)) + + if hasattr(bpy.types, 'ShaderNodeUVMap'): + tex_coord1 = __new_node('ShaderNodeUVMap', (4, -2)) + tex_coord1.uv_map, socketUV1 = 'UV1', 'UV' + else: + tex_coord1 = __new_node('ShaderNodeAttribute', (4, -2)) + tex_coord1.attribute_name, socketUV1 = 'UV1', 'Vector' + + vec_trans = __new_node('ShaderNodeVectorTransform', (1, -1)) + vec_trans.vector_type = 'NORMAL' + vec_trans.convert_from = 'OBJECT' + vec_trans.convert_to = 'CAMERA' + + node_vector = __new_node('ShaderNodeMapping', (2, -1)) + node_vector.vector_type = 'POINT' + node_vector.translation = (0.5, 0.5, 0.0) + node_vector.scale = (0.5, 0.5, 1.0) + + links.new(tex_coord.outputs['Normal'], vec_trans.inputs['Vector']) + links.new(vec_trans.outputs['Vector'], node_vector.inputs['Vector']) + + __new_io(shader.outputs, node_output.inputs, 'Base UV', tex_coord.outputs['UV']) + __new_io(shader.outputs, node_output.inputs, 'Toon UV', node_vector.outputs['Vector']) + __new_io(shader.outputs, node_output.inputs, 'Sphere UV', node_vector.outputs['Vector']) + __new_io(shader.outputs, node_output.inputs, 'SubTex UV', tex_coord1.outputs[socketUV1]) + + return shader + + def __get_shader(self): + group_name = 'MMDShaderDev' + shader = bpy.data.node_groups.get(group_name, None) or bpy.data.node_groups.new(name=group_name, type='ShaderNodeTree') + if len(shader.nodes): + return shader + + nodes, links = shader.nodes, shader.links + + def __new_node(idname, pos): + node = nodes.new(idname) + node.location = (pos[0]*210, pos[1]*220) + return node + + def __new_mix_node(blend_type, pos): + node = __new_node('ShaderNodeMixRGB', pos) + node.blend_type = blend_type + return node + + def __new_math_node(operation, pos): + node = __new_node('ShaderNodeMath', pos) + node.operation = operation + return node + + def __new_io(shader_io, io_sockets, io_name, socket, default_val=None, min_max=None): + links.new(io_sockets[-1], socket) + shader_io[-1].name = io_name + if default_val is not None: + shader_io[-1].default_value = default_val + if min_max is not None: + shader_io[-1].min_value, shader_io[-1].max_value = min_max + + ############################################################################ + node_input = __new_node('NodeGroupInput', (-5, -1)) + node_output = __new_node('NodeGroupOutput', (11, 1)) + + node_diffuse = __new_mix_node('ADD', (-3, 4)) + node_diffuse.use_clamp = True + node_diffuse.inputs['Fac'].default_value = 0.6 + + node_tex = __new_mix_node('MULTIPLY', (-2, 3.5)) + node_toon = __new_mix_node('MULTIPLY', (-1, 3)) + node_sph = __new_mix_node('MULTIPLY', (0, 2.5)) + node_spa = __new_mix_node('ADD', (0, 1.5)) + node_sphere = __new_mix_node('MIX', (1, 1)) + + node_geo = __new_node('ShaderNodeNewGeometry', (6, 3.5)) + node_invert = __new_math_node('LESS_THAN', (7, 3)) + node_cull = __new_math_node('MAXIMUM', (8, 2.5)) + node_alpha = __new_math_node('MINIMUM', (9, 2)) + node_alpha_tex = __new_math_node('MULTIPLY', (-1, -2)) + node_alpha_toon = __new_math_node('MULTIPLY', (0, -2.5)) + node_alpha_sph = __new_math_node('MULTIPLY', (1, -3)) + + node_reflect = __new_math_node('DIVIDE', (7, -1.5)) + node_reflect.use_clamp = True + node_reflect.inputs[0].default_value = 1 + + shader_diffuse = __new_node('ShaderNodeBsdfDiffuse', (8, 0)) + shader_glossy = __new_node('ShaderNodeBsdfGlossy', (8, -1)) + shader_base_mix = __new_node('ShaderNodeMixShader', (9, 0)) + shader_base_mix.inputs['Fac'].default_value = 0.02 + shader_trans = __new_node('ShaderNodeBsdfTransparent', (9, 1)) + shader_alpha_mix = __new_node('ShaderNodeMixShader', (10, 1)) + + links.new(node_reflect.outputs['Value'], shader_glossy.inputs['Roughness']) + links.new(shader_diffuse.outputs['BSDF'], shader_base_mix.inputs[1]) + links.new(shader_glossy.outputs['BSDF'], shader_base_mix.inputs[2]) + + links.new(node_diffuse.outputs['Color'], node_tex.inputs['Color1']) + links.new(node_tex.outputs['Color'], node_toon.inputs['Color1']) + links.new(node_toon.outputs['Color'], node_sph.inputs['Color1']) + links.new(node_toon.outputs['Color'], node_spa.inputs['Color1']) + links.new(node_sph.outputs['Color'], node_sphere.inputs['Color1']) + links.new(node_spa.outputs['Color'], node_sphere.inputs['Color2']) + links.new(node_sphere.outputs['Color'], shader_diffuse.inputs['Color']) + + links.new(node_geo.outputs['Backfacing'], node_invert.inputs[0]) + links.new(node_invert.outputs['Value'], node_cull.inputs[0]) + links.new(node_cull.outputs['Value'], node_alpha.inputs[0]) + links.new(node_alpha_tex.outputs['Value'], node_alpha_toon.inputs[0]) + links.new(node_alpha_toon.outputs['Value'], node_alpha_sph.inputs[0]) + links.new(node_alpha_sph.outputs['Value'], node_alpha.inputs[1]) + + links.new(node_alpha.outputs['Value'], shader_alpha_mix.inputs['Fac']) + links.new(shader_trans.outputs['BSDF'], shader_alpha_mix.inputs[1]) + links.new(shader_base_mix.outputs['Shader'], shader_alpha_mix.inputs[2]) + + ############################################################################ + __new_io(shader.inputs, node_input.outputs, 'Ambient Color', node_diffuse.inputs['Color1'], (0.4, 0.4, 0.4, 1)) + __new_io(shader.inputs, node_input.outputs, 'Diffuse Color', node_diffuse.inputs['Color2'], (0.8, 0.8, 0.8, 1)) + __new_io(shader.inputs, node_input.outputs, 'Specular Color', shader_glossy.inputs['Color'], (0.8, 0.8, 0.8, 1)) + __new_io(shader.inputs, node_input.outputs, 'Reflect', node_reflect.inputs[1], 50, min_max=(1, 512)) + __new_io(shader.inputs, node_input.outputs, 'Base Tex Fac', node_tex.inputs['Fac'], 1) + __new_io(shader.inputs, node_input.outputs, 'Base Tex', node_tex.inputs['Color2'], (1, 1, 1, 1)) + __new_io(shader.inputs, node_input.outputs, 'Toon Tex Fac', node_toon.inputs['Fac'], 1) + __new_io(shader.inputs, node_input.outputs, 'Toon Tex', node_toon.inputs['Color2'], (1, 1, 1, 1)) + __new_io(shader.inputs, node_input.outputs, 'Sphere Tex Fac', node_sph.inputs['Fac'], 1) + __new_io(shader.inputs, node_input.outputs, 'Sphere Tex', node_sph.inputs['Color2'], (1, 1, 1, 1)) + __new_io(shader.inputs, node_input.outputs, 'Sphere Mul/Add', node_sphere.inputs['Fac'], 0) + __new_io(shader.inputs, node_input.outputs, 'Double Sided', node_cull.inputs[1], 0, min_max=(0, 1)) + __new_io(shader.inputs, node_input.outputs, 'Alpha', node_alpha_tex.inputs[0], 1, min_max=(0, 1)) + __new_io(shader.inputs, node_input.outputs, 'Base Alpha', node_alpha_tex.inputs[1], 1, min_max=(0, 1)) + __new_io(shader.inputs, node_input.outputs, 'Toon Alpha', node_alpha_toon.inputs[1], 1, min_max=(0, 1)) + __new_io(shader.inputs, node_input.outputs, 'Sphere Alpha', node_alpha_sph.inputs[1], 1, min_max=(0, 1)) + + links.new(node_input.outputs['Sphere Tex Fac'], node_spa.inputs['Fac']) + links.new(node_input.outputs['Sphere Tex'], node_spa.inputs['Color2']) + + __new_io(shader.outputs, node_output.inputs, 'Shader', shader_alpha_mix.outputs['Shader']) + + return shader + +FnMaterial = _FnMaterialCycles +if bpy.app.version < (2, 80, 0): + FnMaterial = _FnMaterialBI diff --git a/mmd_tools_local/core/vpd/importer.py b/mmd_tools_local/core/vpd/importer.py index f5e4cb00..d4e87308 100644 --- a/mmd_tools_local/core/vpd/importer.py +++ b/mmd_tools_local/core/vpd/importer.py @@ -83,7 +83,7 @@ def __assignToMesh(self, meshObj): for m in self.__vpd_file.morphs: shape_key = key_blocks.get(m.morph_name, None) if shape_key is None: - logging.warning(' * Shape key not found: %s', key_name) + logging.warning(' * Shape key not found: %s', m.morph_name) continue shape_key.value = m.weight diff --git a/mmd_tools_local/operators/model.py b/mmd_tools_local/operators/model.py index 8418d5ff..dc10eaca 100644 --- a/mmd_tools_local/operators/model.py +++ b/mmd_tools_local/operators/model.py @@ -293,27 +293,33 @@ def __configure_rig(self, rig): pose_bone.lock_location = (True, True, True) for m in {x for mesh in meshes for x in mesh.data.materials if x}: - if not hasattr(m, 'use_transparency'): continue mmd_material = m.mmd_material - map_diffuse = next((s.blend_type for s in m.texture_slots if s and s.use_map_color_diffuse), None) - use_diffuse = map_diffuse in {None, 'MULTIPLY'} - diffuse = m.diffuse_color*min(1.0, m.diffuse_intensity/0.8) if use_diffuse else (1.0, 1.0, 1.0) - mmd_material.diffuse_color = diffuse - if self.ambient_color_source == 'MIRROR': - mmd_material.ambient_color = m.mirror_color + if hasattr(m, 'texture_slots'): + map_diffuse = next((s.blend_type for s in m.texture_slots if s and s.use_map_color_diffuse), None) + use_diffuse = map_diffuse in {None, 'MULTIPLY'} + diffuse = m.diffuse_color*min(1.0, m.diffuse_intensity/0.8) if use_diffuse else (1.0, 1.0, 1.0) + mmd_material.diffuse_color = diffuse + if self.ambient_color_source == 'MIRROR': + mmd_material.ambient_color = m.mirror_color + else: + mmd_material.ambient_color = [0.5*c for c in diffuse] + + map_alpha = next((s.blend_type for s in m.texture_slots if s and s.use_map_alpha), None) + if m.use_transparency and map_alpha in {None, 'MULTIPLY'}: + mmd_material.alpha = m.alpha + + mmd_material.specular_color = m.specular_color*min(1.0, m.specular_intensity/0.8) + mmd_material.shininess = m.specular_hardness + mmd_material.is_double_sided = m.game_settings.use_backface_culling + mmd_material.enabled_self_shadow_map = m.use_cast_buffer_shadows and m.alpha > 1e-3 + mmd_material.enabled_self_shadow = m.use_shadows else: + diffuse = m.diffuse_color + mmd_material.diffuse_color = diffuse mmd_material.ambient_color = [0.5*c for c in diffuse] + mmd_material.specular_color = m.specular_color - map_alpha = next((s.blend_type for s in m.texture_slots if s and s.use_map_alpha), None) - if m.use_transparency and map_alpha in {None, 'MULTIPLY'}: - mmd_material.alpha = m.alpha - - mmd_material.specular_color = m.specular_color*min(1.0, m.specular_intensity/0.8) - mmd_material.shininess = m.specular_hardness - mmd_material.is_double_sided = m.game_settings.use_backface_culling - mmd_material.enabled_self_shadow_map = m.use_cast_buffer_shadows and m.alpha > 1e-3 - mmd_material.enabled_self_shadow = m.use_shadows if hasattr(m, 'line_color'): # freestyle line color line_color = list(m.line_color) mmd_material.enabled_toon_edge = line_color[3] >= self.edge_threshold diff --git a/mmd_tools_local/operators/view.py b/mmd_tools_local/operators/view.py index a01fef2d..8358048f 100644 --- a/mmd_tools_local/operators/view.py +++ b/mmd_tools_local/operators/view.py @@ -82,7 +82,7 @@ def execute(self, context): shading.light = 'STUDIO' shading.color_type = 'MATERIAL' shading.show_object_outline = False - context.space_data.overlay.show_backface_culling = False + context.space_data.overlay.show_backface_culling = True return {'FINISHED'} context.scene.render.engine = 'BLENDER_RENDER' @@ -101,7 +101,7 @@ def execute(self, context): except TypeError: pass context.area.spaces[0].viewport_shade='SOLID' - context.area.spaces[0].show_backface_culling = False + context.area.spaces[0].show_backface_culling = True context.scene.game_settings.material_mode = 'MULTITEXTURE' return {'FINISHED'} diff --git a/mmd_tools_local/panels/prop_material.py b/mmd_tools_local/panels/prop_material.py index 819a183c..e8c7bdc2 100644 --- a/mmd_tools_local/panels/prop_material.py +++ b/mmd_tools_local/panels/prop_material.py @@ -114,7 +114,7 @@ def draw(self, context): r.label(icon='ERROR') else: r.operator('mmd_tools.material_open_sphere_texture', text='Add', icon=ICON_FILE_FOLDER) - col.prop(mmd_material, 'sphere_texture_type') + col.row(align=True).prop(mmd_material, 'sphere_texture_type', expand=True) col = layout.column() row = col.row() diff --git a/tools/armature.py b/tools/armature.py index ef5f77ba..0b74a493 100644 --- a/tools/armature.py +++ b/tools/armature.py @@ -60,7 +60,7 @@ class FixArmature(bpy.types.Operator): @classmethod def poll(cls, context): - if tools.common.get_armature() is None: + if not tools.common.get_armature(): return False if len(tools.common.get_armature_objects()) == 0: @@ -385,21 +385,21 @@ def execute(self, context): # TODO # Makes all materials visible - for i, mat_slot in enumerate(mesh.material_slots): - context.object.active_material_index = i - bpy.context.object.active_material.blend_method = 'OPAQUE' - - # bpy.data.node_groups["Shader Nodetree"].nodes["Principled BSDF"].inputs[5].default_value = 0 - from bpy_extras.node_shader_utils import PrincipledBSDFWrapper - shader = PrincipledBSDFWrapper(mat_slot.material, is_readonly=False) - if i == 0: - for atr in dir(shader): - print(atr, getattr(shader, atr)) - shader.specular = 0 - shader.metallic = 0 - shader.roughness = 1 - #shader.transmission = 0 - #shader.transmission_roughness = 0 + # for i, mat_slot in enumerate(mesh.material_slots): + # context.object.active_material_index = i + # bpy.context.object.active_material.blend_method = 'OPAQUE' + # + # # bpy.data.node_groups["Shader Nodetree"].nodes["Principled BSDF"].inputs[5].default_value = 0 + # from bpy_extras.node_shader_utils import PrincipledBSDFWrapper + # shader = PrincipledBSDFWrapper(mat_slot.material, is_readonly=False) + # if i == 0: + # for atr in dir(shader): + # print(atr, getattr(shader, atr)) + # shader.specular = 0 + # shader.metallic = 0 + # shader.roughness = 1 + # #shader.transmission = 0 + # #shader.transmission_roughness = 0 for area in context.screen.areas: # iterate through areas in current screen if area.type == 'VIEW_3D': diff --git a/tools/armature_manual.py b/tools/armature_manual.py index 071ae9f8..55170802 100644 --- a/tools/armature_manual.py +++ b/tools/armature_manual.py @@ -137,7 +137,7 @@ def execute(self, context): if version_2_79_or_older(): bpy.context.space_data.transform_manipulators = {'TRANSLATE'} else: - bpy.ops.wm.tool_set_by_name(name="Cursor") + bpy.ops.wm.tool_set_by_name(name="Select Box") tools.eyetracking.eye_left = None diff --git a/tools/common.py b/tools/common.py index c62fe98a..021c05fe 100644 --- a/tools/common.py +++ b/tools/common.py @@ -162,7 +162,9 @@ def set_unselectable(obj, val=True): def switch(new_mode): - if get_active().mode != new_mode and bpy.ops.object.mode_set.poll(): + if get_active() and get_active().mode == new_mode: + return + if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode=new_mode, toggle=False) @@ -1437,23 +1439,28 @@ def matmul(a, b): def set_cats_verion_string(): version_str = '' + version_temp = [] + + for n in globs.version: + version_temp.append(n) + # if in dev version, increase version - if globs.dev_branch and len(globs.version) > 2: - globs.version[2] += 1 + if globs.dev_branch and len(version_temp) > 2: + version_temp[2] += 1 # Convert version to string - if len(globs.version) > 0: - version_str += str(globs.version[0]) - for index, i in enumerate(globs.version): + if len(version_temp) > 0: + version_str += str(version_temp[0]) + for index, i in enumerate(version_temp): if index == 0: continue - version_str += '.' + str(globs.version[index]) + version_str += '.' + str(version_temp[index]) if globs.dev_branch: version_str += '-dev' # Change the version back - if globs.dev_branch and len(globs.version) > 2: - globs.version[2] -= 1 + if globs.dev_branch and len(version_temp) > 2: + version_temp[2] -= 1 globs.version_str = version_str diff --git a/tools/importer.py b/tools/importer.py index 7f046584..2b54102a 100644 --- a/tools/importer.py +++ b/tools/importer.py @@ -76,7 +76,7 @@ class ImportAnyModel(bpy.types.Operator, bpy_extras.io_utils.ImportHelper): ) def execute(self, context): - print(self.directory) + # print(self.directory) tools.common.remove_unused_objects() # Make sure that the first layer is visible