diff --git a/io_scene_niftools/addon_updater_ops.py b/io_scene_niftools/addon_updater_ops.py index e254e1cb0..efba015c6 100644 --- a/io_scene_niftools/addon_updater_ops.py +++ b/io_scene_niftools/addon_updater_ops.py @@ -424,7 +424,7 @@ def draw(self, context): return # use a "failed flag"? it shows this label if the case failed. - if self.error is not "": + if self.error != "": col = layout.column() col.scale_y = 0.7 col.label(text="There was an issue trying to auto-install", icon="ERROR") @@ -1102,7 +1102,7 @@ def update_settings_ui(self, context, element=None): last_check = updater.json["last_check"] if updater.error is not None and updater.error_msg is not None: row.label(text=updater.error_msg) - elif last_check is not "" and last_check is not None: + elif last_check != "" and last_check is not None: last_check = last_check[0: last_check.index(".")] row.label(text="Last update check: " + last_check) else: diff --git a/io_scene_niftools/modules/nif_export/armature/__init__.py b/io_scene_niftools/modules/nif_export/armature/__init__.py index 7c8311015..c847ba8f9 100644 --- a/io_scene_niftools/modules/nif_export/armature/__init__.py +++ b/io_scene_niftools/modules/nif_export/armature/__init__.py @@ -43,6 +43,7 @@ from io_scene_niftools.utils import math from io_scene_niftools.utils.singleton import NifOp from io_scene_niftools.utils.logging import NifLog +from pyffi.formats.nif import NifFormat def get_bind_data(b_armature): @@ -68,11 +69,14 @@ def export_bones(self, b_obj, n_root_node): self.b_action = self.transform_anim.get_active_action(b_obj) # the armature b_obj was already exported as a NiNode ("Scene Root") n_root_node # export the bones as NiNodes, starting from root bones + old_position = b_obj.data.pose_position + b_obj.data.pose_position = 'POSE' for b_bone in b_obj.data.bones.values(): if not b_bone.parent: - self.export_bone(b_obj, b_bone, n_root_node) + self.export_bone(b_obj, b_bone, n_root_node, n_root_node) + b_obj.data.pose_position = old_position - def export_bone(self, b_obj, b_bone, n_parent_node): + def export_bone(self, b_obj, b_bone, n_parent_node, n_root_node): """Exports a bone and all of its children.""" # create a new nif block for this b_bone n_node = types.create_ninode(b_bone) @@ -81,14 +85,18 @@ def export_bone(self, b_obj, b_bone, n_parent_node): n_parent_node.add_child(n_node) self.export_bone_flags(b_bone, n_node) - # rest pose - math.set_object_matrix(b_bone, n_node) + # set the pose on the nodes + nif_matrix = NifFormat.Matrix44() + nif_matrix.set_rows(*math.blender_bind_to_nif_bind(b_obj.pose.bones[b_bone.name].matrix).transposed()) + # make the transform relative to the parent, rather than the armature + nif_matrix *= n_parent_node.get_transform(n_root_node).get_inverse(fast=False) + n_node.set_transform(nif_matrix) # per-bone animation self.transform_anim.export_transforms(n_node, b_obj, self.b_action, b_bone) # continue down the bone tree for b_child in b_bone.children: - self.export_bone(b_obj, b_child, n_node) + self.export_bone(b_obj, b_child, n_node, n_root_node) def export_bone_flags(self, b_bone, n_node): """Exports or sets the flags according to the custom data in b_bone or the game version if none was set""" diff --git a/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py b/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py index 83b73357f..6e8956ca6 100644 --- a/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py +++ b/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py @@ -117,7 +117,7 @@ def export_tri_shapes(self, b_obj, n_parent, n_root, trishape_name=None): mesh_hasnormals = False if b_mat is not None: mesh_hasnormals = True # for proper lighting - if (game == 'SKYRIM') and (b_mat.niftools_shader.bslsp_shaderobjtype == 'Skin Tint'): + if (game == 'SKYRIM') and (b_mat.niftools_shader.bslsp_shaderobjtype in ('Skin Tint', 'Face Tint')): mesh_hasnormals = False # for proper lighting # create a trishape block @@ -452,8 +452,8 @@ def export_tri_shapes(self, b_obj, n_parent, n_root, trishape_name=None): # update bind position skinning data # trishape.update_bind_position() - # override pyffi ttrishape.update_bind_position with custom one that is relative to the nif root - self.update_bind_position(trishape, n_root) + # override pyffi trishape.update_bind_position with custom one that is relative to the nif root + self.update_bind_position(trishape, n_root, b_obj_armature) # calculate center and radius for each skin bone data block trishape.update_skin_center_radius() @@ -501,12 +501,12 @@ def export_tri_shapes(self, b_obj, n_parent, n_root, trishape_name=None): self.morph_anim.export_morph(b_mesh, trishape, vertex_map) return trishape - def update_bind_position(self, n_geom, n_root): - """Make current position of the bones the bind position for this geometry. + def update_bind_position(self, n_geom, n_root, b_obj_armature): + """Transfer the Blender bind position to the nif bind position. Sets the NiSkinData overall transform to the inverse of the geometry transform relative to the skeleton root, and sets the NiSkinData of each bone to - the geometry transform relative to the skeleton root times the inverse of the bone - transform relative to the skeleton root.""" + the inverse of the transpose of the bone transform relative to the skeleton root, corrected + for the overall transform.""" if not n_geom.is_skin(): return @@ -516,23 +516,32 @@ def update_bind_position(self, n_geom, n_root): skindata = skininst.data skelroot = skininst.skeleton_root - # calculate overall offset - geomtransform = n_geom.get_transform(skelroot) - skindata.set_transform(geomtransform.get_inverse()) + # calculate overall offset (including the skeleton root transform) and use its inverse + geomtransform = (n_geom.get_transform(skelroot) * skelroot.get_transform()).get_inverse(fast=False) + skindata.set_transform(geomtransform) # for some nifs, somehow n_root is not set properly?! if not n_root: NifLog.warn(f"n_root was not set, bug") n_root = skelroot + old_position = b_obj_armature.data.pose_position + b_obj_armature.data.pose_position = 'POSE' + # calculate bone offsets for i, bone in enumerate(skininst.bones): + bone_name = block_store.block_to_obj[bone].name + pose_bone = b_obj_armature.pose.bones[bone_name] + n_bind = NifFormat.Matrix44() + n_bind.set_rows(*math.blender_bind_to_nif_bind(pose_bone.matrix).transposed()) # todo [armature] figure out the correct transform that works universally # inverse skin bind in nif armature space, relative to root / geom?? - skindata.bone_list[i].set_transform(geomtransform * bone.get_transform(n_root).get_inverse()) + skindata.bone_list[i].set_transform((n_bind * geomtransform).get_inverse(fast=False)) # this seems to be correct for skyrim heads, but breaks stuff like ZT2 elephant # skindata.bone_list[i].set_transform(bone.get_transform(n_root).get_inverse()) + b_obj_armature.data.pose_position = old_position + def get_bone_block(self, b_bone): """For a blender bone, return the corresponding nif node from the blocks that have already been exported""" for n_block, b_obj in block_store.block_to_obj.items(): @@ -720,8 +729,8 @@ def get_triangulated_mesh(self, b_obj): # get the armature influencing this mesh, if it exists b_armature_obj = b_obj.find_armature() if b_armature_obj: - for pbone in b_armature_obj.pose.bones: - pbone.matrix_basis = mathutils.Matrix() + old_position = b_armature_obj.data.pose_position + b_armature_obj.data.pose_position = 'REST' # make sure the model has a triangulation modifier self.ensure_tri_modifier(b_obj) @@ -729,7 +738,10 @@ def get_triangulated_mesh(self, b_obj): # make a copy with all modifiers applied dg = bpy.context.evaluated_depsgraph_get() eval_obj = b_obj.evaluated_get(dg) - return eval_obj.to_mesh(preserve_all_data_layers=True, depsgraph=dg) + eval_mesh = eval_obj.to_mesh(preserve_all_data_layers=True, depsgraph=dg) + if b_armature_obj: + b_armature_obj.data.pose_position = old_position + return eval_mesh def ensure_tri_modifier(self, b_obj): """Makes sure that ob has a triangulation modifier in its stack.""" diff --git a/io_scene_niftools/modules/nif_export/object/__init__.py b/io_scene_niftools/modules/nif_export/object/__init__.py index 81f181fde..06c586691 100644 --- a/io_scene_niftools/modules/nif_export/object/__init__.py +++ b/io_scene_niftools/modules/nif_export/object/__init__.py @@ -140,9 +140,9 @@ def set_node_flags(self, b_obj, n_node): n_node.flags = 0x000E elif game in ('SID_MEIER_S_RAILROADS', 'CIVILIZATION_IV'): n_node.flags = 0x0010 - elif game is 'EMPIRE_EARTH_II': + elif game == 'EMPIRE_EARTH_II': n_node.flags = 0x0002 - elif game is 'DIVINITY_2': + elif game == 'DIVINITY_2': n_node.flags = 0x0310 else: n_node.flags = 0x000C # morrowind @@ -177,7 +177,10 @@ def export_node(self, b_obj, n_parent): # If this has children or animations or more than one material it gets wrapped in a purpose made NiNode. if not (b_action or b_obj.children or is_multimaterial or has_track): - return self.mesh_helper.export_tri_shapes(b_obj, n_parent, self.n_root, b_obj.name) + mesh = self.mesh_helper.export_tri_shapes(b_obj, n_parent, self.n_root, b_obj.name) + if not self.n_root: + self.n_root = mesh + return mesh # set transform on trishapes rather than NiNodes for skinned meshes to fix an issue with clothing slots if b_obj.parent and b_obj.parent.type == 'ARMATURE' and b_action: diff --git a/io_scene_niftools/modules/nif_export/property/shader/__init__.py b/io_scene_niftools/modules/nif_export/property/shader/__init__.py index 195e67500..39cd78941 100644 --- a/io_scene_niftools/modules/nif_export/property/shader/__init__.py +++ b/io_scene_niftools/modules/nif_export/property/shader/__init__.py @@ -80,9 +80,7 @@ def export_bs_effect_shader_property(self, b_mat): # bsshader.alpha = (1 - b_mat.alpha) # Emissive - bsshader.emissive_color.r = b_mat.niftools.emissive_color.r - bsshader.emissive_color.g = b_mat.niftools.emissive_color.g - bsshader.emissive_color.b = b_mat.niftools.emissive_color.b + BSShaderProperty.set_color3_property(bsshader.emissive_color, b_mat.niftools.emissive_color) bsshader.emissive_color.a = b_mat.niftools.emissive_alpha.v # TODO [shader] Expose a emission multiplier value # bsshader.emissive_multiple = b_mat.emit @@ -101,9 +99,11 @@ def export_bs_lighting_shader_property(self, b_mat): # Diffuse color d = b_mat.diffuse_color - bsshader.skin_tint_color.r = d[0] - bsshader.skin_tint_color.g = d[1] - bsshader.skin_tint_color.b = d[2] + + if b_s_type == NifFormat.BSLightingShaderPropertyShaderType["Skin Tint"]: + BSShaderProperty.set_color3_property(bsshader.skin_tint_color, d) + elif b_s_type == NifFormat.BSLightingShaderPropertyShaderType["Hair Tint"]: + BSShaderProperty.set_color3_property(bsshader.hair_tint_color, d) # TODO [shader] expose intensity value # b_mat.diffuse_intensity = 1.0 @@ -111,10 +111,7 @@ def export_bs_lighting_shader_property(self, b_mat): bsshader.lighting_effect_2 = b_mat.niftools.lightingeffect2 # Emissive - e = b_mat.niftools.emissive_color - bsshader.emissive_color.r = e[0] - bsshader.emissive_color.g = e[1] - bsshader.emissive_color.b = e[2] + BSShaderProperty.set_color3_property(bsshader.emissive_color, b_mat.niftools.emissive_color) # TODO [shader] Expose a emission multiplier value # bsshader.emissive_multiple = b_mat.emit @@ -122,10 +119,7 @@ def export_bs_lighting_shader_property(self, b_mat): bsshader.glossiness = 1/b_mat.roughness - 1 if b_mat.roughness != 0 else FLOAT_MAX # Specular color - s = b_mat.specular_color - bsshader.specular_color.r = s[0] - bsshader.specular_color.g = s[1] - bsshader.specular_color.b = s[2] + BSShaderProperty.set_color3_property(bsshader.specular_color, b_mat.specular_color) bsshader.specular_strength = b_mat.specular_intensity # Alpha @@ -175,3 +169,9 @@ def process_flags(b_mat, flags): if b_flag: sf_flag_index = flags._names.index(sf_flag) flags._items[sf_flag_index]._value = 1 + + @staticmethod + def set_color3_property(n_property, b_color): + n_property.r = b_color[0] + n_property.g = b_color[1] + n_property.b = b_color[2] diff --git a/io_scene_niftools/modules/nif_export/property/texture/types/bsshadertexture.py b/io_scene_niftools/modules/nif_export/property/texture/types/bsshadertexture.py index 04b394e84..4e61c96bc 100644 --- a/io_scene_niftools/modules/nif_export/property/texture/types/bsshadertexture.py +++ b/io_scene_niftools/modules/nif_export/property/texture/types/bsshadertexture.py @@ -80,8 +80,8 @@ def export_bs_lighting_shader_prop_textures(self, bsshader): texset.num_textures = 9 texset.textures.update_size() - if self.slots[TEX_SLOTS.DETAIL]: - texset.textures[6] = TextureWriter.export_texture_filename(self.slots[TEX_SLOTS.DETAIL]) + if self.slots[TEX_SLOTS.DECAL_0]: + texset.textures[6] = TextureWriter.export_texture_filename(self.slots[TEX_SLOTS.DECAL_0]) if self.slots[TEX_SLOTS.GLOSS]: texset.textures[7] = TextureWriter.export_texture_filename(self.slots[TEX_SLOTS.GLOSS]) diff --git a/io_scene_niftools/modules/nif_export/property/texture/writer.py b/io_scene_niftools/modules/nif_export/property/texture/writer.py index 8e46277f6..81d033930 100644 --- a/io_scene_niftools/modules/nif_export/property/texture/writer.py +++ b/io_scene_niftools/modules/nif_export/property/texture/writer.py @@ -122,7 +122,7 @@ def export_texture_filename(b_texture_node): # try and find a DDS alternative, force it if required ddsfilename = f"{(filename[:-4])}.dds" - if os.path.exists(ddsfilename) or NifOp.props.force_dds: + if os.path.exists(bpy.path.abspath(ddsfilename)) or NifOp.props.force_dds: filename = ddsfilename # sanitize file path @@ -136,6 +136,8 @@ def export_texture_filename(b_texture_node): idx = filename.find("textures") if idx >= 0: filename = filename[idx:] + elif not os.path.exists(bpy.path.abspath(filename)): + pass else: NifLog.warn(f"{filename} does not reside in a 'Textures' folder; texture path will be stripped and textures may not display in-game") filename = os.path.basename(filename) diff --git a/io_scene_niftools/modules/nif_import/armature/__init__.py b/io_scene_niftools/modules/nif_import/armature/__init__.py index 02bb61df0..936af2f08 100644 --- a/io_scene_niftools/modules/nif_import/armature/__init__.py +++ b/io_scene_niftools/modules/nif_import/armature/__init__.py @@ -243,11 +243,9 @@ def import_bone_bind(self, n_block, b_armature_data, n_armature, b_parent_bone=N # get transformation in blender's coordinate space b_bind = math.nif_bind_to_blender_bind(n_bind) - # the following is a workaround because blender can no longer set matrices to bones directly - tail, roll = bpy.types.Bone.AxisRollFromMatrix(b_bind.to_3x3()) - b_edit_bone.head = b_bind.to_translation() - b_edit_bone.tail = tail + b_edit_bone.head - b_edit_bone.roll = roll + # set the bone matrix - but set the tail first to prevent issues with zero-length bone + b_edit_bone.tail = mathutils.Vector([0, 0, 1]) + b_edit_bone.matrix = b_bind # link to parent if b_parent_bone: b_edit_bone.parent = b_parent_bone diff --git a/io_scene_niftools/modules/nif_import/object/__init__.py b/io_scene_niftools/modules/nif_import/object/__init__.py index d42bcbef3..d929bfa4a 100644 --- a/io_scene_niftools/modules/nif_import/object/__init__.py +++ b/io_scene_niftools/modules/nif_import/object/__init__.py @@ -56,7 +56,7 @@ def __init__(self): def create_b_obj(n_block, b_obj_data, name=""): """Helper function to create a b_obj from b_obj_data, link it to the current scene, make it active and select it.""" # get the actual nif name - n_name = n_block.name.decode() if n_block else "" + n_name = block_store.import_name(n_block) if name: n_name = name # let blender choose a name diff --git a/io_scene_niftools/modules/nif_import/property/nodes_wrapper/__init__.py b/io_scene_niftools/modules/nif_import/property/nodes_wrapper/__init__.py index 11d921d6c..4178f5153 100644 --- a/io_scene_niftools/modules/nif_import/property/nodes_wrapper/__init__.py +++ b/io_scene_niftools/modules/nif_import/property/nodes_wrapper/__init__.py @@ -336,11 +336,13 @@ def link_normal_node(self, b_texture_node): group_node = nodes.new('ShaderNodeGroup') group_node.node_tree = node_group links.new(group_node.inputs[0], b_texture_node.outputs[0]) - # create tangent normal map converter and link to it - tangent_converter = nodes.new("ShaderNodeNormalMap") - self.tree.links.new(tangent_converter.inputs[1], group_node.outputs[0]) - # link to the diffuse shader - self.tree.links.new(self.diffuse_shader.inputs[2], tangent_converter.outputs[0]) + if self.b_mat.niftools_shader.slsf_1_model_space_normals: + self.tree.links.new(self.diffuse_shader.inputs[2], group_node.outputs[0]) + else: + # create tangent normal map converter and link to it + tangent_converter = nodes.new("ShaderNodeNormalMap") + self.tree.links.new(tangent_converter.inputs[1], group_node.outputs[0]) + self.tree.links.new(self.diffuse_shader.inputs[2], tangent_converter.outputs[0]) # # Influence mapping # b_texture_node.texture.use_normal_map = True # causes artifacts otherwise. # diff --git a/io_scene_niftools/modules/nif_import/property/shader/bsshaderproperty.py b/io_scene_niftools/modules/nif_import/property/shader/bsshaderproperty.py index 1fb5fe53f..d91374283 100644 --- a/io_scene_niftools/modules/nif_import/property/shader/bsshaderproperty.py +++ b/io_scene_niftools/modules/nif_import/property/shader/bsshaderproperty.py @@ -102,10 +102,9 @@ def import_bs_lighting_shader_property(self, bs_shader_property): self._nodes_wrapper.global_uv_offset_scale(x_scale, y_scale, x_offset, y_offset, clamp_x, clamp_y) # Diffuse color - if bs_shader_property.skin_tint_color: + if shader_type == NifFormat.BSLightingShaderPropertyShaderType["Skin Tint"]: Material.import_material_diffuse(self._b_mat, bs_shader_property.skin_tint_color) - - if (self._b_mat.diffuse_color[0] + self._b_mat.diffuse_color[1] + self._b_mat.diffuse_color[2]) == 0: + elif shader_type == NifFormat.BSLightingShaderPropertyShaderType["Hair Tint"]: Material.import_material_diffuse(self._b_mat, bs_shader_property.hair_tint_color) # TODO [material][b_shader][property] Handle nialphaproperty node lookup diff --git a/io_scene_niftools/modules/nif_import/property/texture/loader.py b/io_scene_niftools/modules/nif_import/property/texture/loader.py index 3c4a6a8cd..f395e8708 100644 --- a/io_scene_niftools/modules/nif_import/property/texture/loader.py +++ b/io_scene_niftools/modules/nif_import/property/texture/loader.py @@ -262,6 +262,12 @@ def import_external_source(self, source): # go through all texture search paths for texdir in search_path_list: + if texdir[0:2] == "//": + # Blender-specific directory, slows down resolve_ncase: + relative = True + texdir = texdir[2:] + else: + relative = False texdir = texdir.replace('\\', os.sep) texdir = texdir.replace('/', os.sep) # go through all possible file names, try alternate extensions too; for linux, also try lower case versions of filenames @@ -281,10 +287,15 @@ def import_external_source(self, source): tex = os.path.join(texdir, texfn) # "ignore case" on linuxW + if relative: + tex = bpy.path.abspath("//" + tex) tex = bpy.path.resolve_ncase(tex) NifLog.debug(f"Searching {tex}") if os.path.exists(tex): - return self.load_image(tex) + if relative: + return self.load_image(bpy.path.relpath(tex)) + else: + return self.load_image(tex) else: tex = fn diff --git a/io_scene_niftools/nif_export.py b/io_scene_niftools/nif_export.py index b5a12d06c..df1054c2e 100644 --- a/io_scene_niftools/nif_export.py +++ b/io_scene_niftools/nif_export.py @@ -100,11 +100,7 @@ def execute(self): return {'FINISHED'} for b_obj in self.exportable_objects: - # armatures should not be in rest position - if b_obj.type == 'ARMATURE': - b_obj.data.pose_position = 'POSE' - - elif b_obj.type == 'MESH': + if b_obj.type == 'MESH': if b_obj.parent and b_obj.parent.type == 'ARMATURE': for b_mod in b_obj.modifiers: if b_mod.type == 'ARMATURE' and b_mod.use_bone_envelopes: @@ -113,7 +109,7 @@ def execute(self): f"convert their envelopes to vertex weights, and turn off envelopes.") # check for non-uniform transforms - scale = b_obj.matrix_local.to_scale() + scale = b_obj.scale if abs(scale.x - scale.y) > NifOp.props.epsilon or abs(scale.y - scale.z) > NifOp.props.epsilon: NifLog.warn(f"Non-uniform scaling not supported.\n" f"Workaround: apply size and rotation (CTRL-A) on '{b_obj.name}'.") diff --git a/io_scene_niftools/properties/collision.py b/io_scene_niftools/properties/collision.py index f5a860dfb..877a9550a 100644 --- a/io_scene_niftools/properties/collision.py +++ b/io_scene_niftools/properties/collision.py @@ -80,7 +80,6 @@ class CollisionProperty(PropertyGroup): name='Collision layer', description='Collision layer string (game-specific)', items=game_specific_col_layer_items, - default=0 ) deactivator_type: EnumProperty( diff --git a/io_scene_niftools/properties/shader.py b/io_scene_niftools/properties/shader.py index c7f08e711..274df5f5f 100644 --- a/io_scene_niftools/properties/shader.py +++ b/io_scene_niftools/properties/shader.py @@ -77,389 +77,26 @@ class ShaderProps(PropertyGroup): # default = 'SHADER_DEFAULT' ) - sf_specular: BoolProperty( - name='Specular' - ) - - sf_skinned: BoolProperty( - name='Skinned' - ) - - sf_low_detail: BoolProperty( - name='Low Detail' - ) - - sf_vertex_alpha: BoolProperty( - name='Vertex Alpha' - ) - - sf_unknown_1: BoolProperty( - name='Unknown 1' - ) - - sf_single_pass: BoolProperty( - name='Single Pass' - ) - - sf_empty: BoolProperty( - name='Empty' - ) - - sf_environment_mapping: BoolProperty( - name='Environment Mapping' - ) - - sf_alpha_texture: BoolProperty( - name='Alpha Texture' - ) - - sf_unknown_2: BoolProperty( - name='Unknown 2' - ) - - sf_face_gen: BoolProperty( - name='Face Gen' - ) - - sf_parallax_shader_index_15: BoolProperty( - name='Parallax Shader Index' - ) - - sf_unknown_3: BoolProperty( - name='Unknown 3' - ) - - sf_non_projective_shadows: BoolProperty( - name='Non-Projective Shadows' - ) - - sf_unknown_4: BoolProperty( - name='Unknown 4' - ) - - sf_refraction: BoolProperty( - name='Refraction' - ) - - sf_fire_refraction: BoolProperty( - name='Fire Refraction' - ) - - sf_eye_environment_mapping: BoolProperty( - name='Eye Environment Mapping' - ) - - sf_hair: BoolProperty( - name='Hair' - ) - - sf_dynamic_alpha: BoolProperty( - name='Dynamic Alpha' - ) - - sf_localmap_hide_secret: BoolProperty( - name='Local Map Hide Secret' - ) - - sf_window_environment_mapping: BoolProperty( - name='Window Environment Mapping' - ) - - sf_tree_billboard: BoolProperty( - name='Tree Billboard' - ) - - sf_shadow_frustum: BoolProperty( - name='Shadow Frustum' - ) - - sf_multiple_textures: BoolProperty( - name='Multiple Textures' - ) - - sf_remappable_textures: BoolProperty( - name='Remappable Textures' - ) - - sf_decal_single_pass: BoolProperty( - name='Decal Single Pass' - ) - - sf_dynamic_decal_single_pass: BoolProperty( - name='Dynamic Decal Single Pass' - ) - - sf_parallax_occulsion: BoolProperty( - name='Parallax Occlusion' - ) - - sf_external_emittance: BoolProperty( - name='External Emittance' - ) - - sf_shadow_map: BoolProperty( - name='Shadow Map' - ) - - sf_z_buffer_test: BoolProperty( - name='Z Buffer Test' - ) - - slsf_1_specular: BoolProperty( - name='Specular' - ) - - slsf_1_skinned: BoolProperty( - name='Skinned' - ) - - slsf_1_temp_refraction: BoolProperty( - name='Temp Refraction' - ) - - slsf_1_vertex_alpha: BoolProperty( - name='Vertex Alpha' - ) - - slsf_1_greyscale_to_palettecolor: BoolProperty( - name='Greyscale to Palette Color' - ) - - slsf_1_greyscale_to_palettealpha: BoolProperty( - name='Greyscale to Palette Alpha' - ) - - slsf_1_use_falloff: BoolProperty( - name='Use Falloff' - ) - - slsf_1_environment_mapping: BoolProperty( - name='Environment Mapping' - ) - - slsf_1_recieve_shadows: BoolProperty( - name='Receive Shadows' - ) - - slsf_1_cast_shadows: BoolProperty( - name='Cast Shadows' - ) - - slsf_1_facegen_detail: BoolProperty( - name='Facegen Detail' - ) - - slsf_1_Parallax: BoolProperty( - name='Parallax' - ) - - slsf_1_model_space_normals: BoolProperty( - name='Model Space Normals' - ) - - slsf_1_non_projective_shadows: BoolProperty( - name='Non Projective Shadows' - ) - - slsf_1_Landscape: BoolProperty( - name='Landscape' - ) - - slsf_1_refraction: BoolProperty( - name='Refraction' - ) - slsf_1_fire_refraction: BoolProperty( - name='Fire Refraction' - ) - - slsf_1_eye_environment_mapping: BoolProperty( - name='Eye Environment Mapping' - ) - - slsf_1_hair_soft_lighting: BoolProperty( - name='Hair Soft Lighting' - ) - - slsf_1_screendoor_alpha_fade: BoolProperty( - name='Screendoor Alpha Fade' - ) - - slsf_1_localmap_hide_secret: BoolProperty( - name='Localmap Hide Secret' - ) - - slsf_1_facegen_rgb_tint: BoolProperty( - name='Facegen RGB Tint' - ) - - slsf_1_own_emit: BoolProperty( - name='Own Emit' - ) - - slsf_1_projected_uv: BoolProperty( - name='Projected UV' - ) - - slsf_1_multiple_textures: BoolProperty( - name='Multiple Textures' - ) - - slsf_1_remappable_textures: BoolProperty( - name='Remappable Textures' - ) - - slsf_1_decal: BoolProperty( - name='Decal' - ) - - slsf_1_dynamic_decal: BoolProperty( - name='Dynamic Decal' - ) - - slsf_1_parallax_occlusion: BoolProperty( - name='Parallax Occlusion' - ) - - slsf_1_external_emittance: BoolProperty( - name='External Emittance' - ) - - slsf_1_soft_effect: BoolProperty( - name='Soft Effect' - ) - - slsf_1_z_buffer_test: BoolProperty( - name='ZBuffer Test' - ) - - slsf_2_z_buffer_write: BoolProperty( - name='ZBuffer Write' - ) - - slsf_2_lod_landscape: BoolProperty( - name='LOD Landscape' - ) - - slsf_2_lod_objects: BoolProperty( - name='LOD Objects' - ) - - slsf_2_no_fade: BoolProperty( - name='No Fade' - ) - - slsf_2_double_sided: BoolProperty( - name='Double Sided' - ) - - slsf_2_vertex_colors: BoolProperty( - name='Vertex Colors' - ) - - slsf_2_glow_map: BoolProperty( - name='Glow Map' - ) - - slsf_2_assume_shadowmask: BoolProperty( - name='Assume Shadowmask' - ) - - slsf_2_packed_tangent: BoolProperty( - name='Packed Tangent' - ) - - slsf_2_multi_index_snow: BoolProperty( - name='Multi Index Snow' - ) - - slsf_2_vertex_lighting: BoolProperty( - name='Vertex Lighting' - ) - - slsf_2_uniform_scale: BoolProperty( - name='Uniform Scale' - ) - - slsf_2_fit_slope: BoolProperty( - name='Fit Slope' - ) - - slsf_2_billboard: BoolProperty( - name='Billboard' - ) - - slsf_2_no_lod_land_blend: BoolProperty( - name='No LOD Land Blend' - ) - - slsf_2_env_map_light_fade: BoolProperty( - name='Envmap Light Fade' - ) - - slsf_2_wireframe: BoolProperty( - name='Wireframe' - ) - - slsf_2_weapon_blood: BoolProperty( - name='Weapon Blood' - ) - - slsf_2_hide_on_local_map: BoolProperty( - name='Hide On Local Map' - ) - - slsf_2_premult_alpha: BoolProperty( - name='Premult Alpha' - ) - - slsf_2_cloud_lod: BoolProperty( - name='Cloud Lod' - ) - - slsf_2_anisotropic_lighting: BoolProperty( - name='Anisotropic Lighting' - ) - - slsf_2_no_transparency_multisampling: BoolProperty( - name='No Transparency Multisampling' - ) - - slsf_2_unused01: BoolProperty( - name='Unused01' - ) - - slsf_2_multi_layer_parallax: BoolProperty( - name='Multi Layer Parallax' - ) - - slsf_2_soft_lighting: BoolProperty( - name='Soft Lighting' - ) - - slsf_2_rim_lighting: BoolProperty( - name='Rim Lighting' - ) - - slsf_2_back_lighting: BoolProperty( - name='Back Lighting' - ) - - slsf_2_unused02: BoolProperty( - name='Unused02' - ) - - slsf_2_tree_anim: BoolProperty( - name='Tree Anim' - ) - - slsf_2_effect_lighting: BoolProperty( - name='Effect Lighting' - ) - - slsf_2_hd_lod_objects: BoolProperty( - name='HD LOD Objects' - ) +def prettify_prop_name(property_name): + replacers = [('Hd', 'HD'), ('Lod', 'LOD')] + prettified = ' '.join([word.capitalize() for word in property_name.split('_')]) + for original, replacement in replacers: + prettified = prettified.replace(original, replacement) + return prettified + + +annotations_dict = ShaderProps.__dict__.get('__annotations__', None) +if annotations_dict: + for property_name in NifFormat.BSShaderFlags._names: + if property_name not in annotations_dict: + annotations_dict[property_name] = BoolProperty(name=prettify_prop_name(property_name[3:])) + for property_name in NifFormat.SkyrimShaderPropertyFlags1._names: + if property_name not in annotations_dict: + annotations_dict[property_name] = BoolProperty(name=prettify_prop_name(property_name[7:])) + for property_name in NifFormat.SkyrimShaderPropertyFlags2._names: + if property_name not in annotations_dict: + annotations_dict[property_name] = BoolProperty(name=prettify_prop_name(property_name[7:])) CLASSES = [ diff --git a/io_scene_niftools/ui/shader.py b/io_scene_niftools/ui/shader.py index 2fb1475a7..17c757028 100644 --- a/io_scene_niftools/ui/shader.py +++ b/io_scene_niftools/ui/shader.py @@ -39,6 +39,8 @@ from bpy.types import Panel +from pyffi.formats.nif import NifFormat + from io_scene_niftools.utils.decorators import register_classes, unregister_classes @@ -67,106 +69,16 @@ def draw(self, context): if nif_obj_props.bs_shadertype == 'BSShaderPPLightingProperty': row.prop(nif_obj_props, "bsspplp_shaderobjtype") - row.prop(nif_obj_props, "sf_alpha_texture") - row.prop(nif_obj_props, "sf_decal_single_pass") - row.prop(nif_obj_props, "sf_dynamic_alpha") - row.prop(nif_obj_props, "sf_dynamic_decal_single_pass") - row.prop(nif_obj_props, "sf_empty") - row.prop(nif_obj_props, "sf_environment_mapping") - row.prop(nif_obj_props, "sf_external_emittance") - row.prop(nif_obj_props, "sf_eye_environment_mapping") - row.prop(nif_obj_props, "sf_face_gen") - row.prop(nif_obj_props, "sf_fire_refraction") - row.prop(nif_obj_props, "sf_hair") - row.prop(nif_obj_props, "sf_localmap_hide_secret") - row.prop(nif_obj_props, "sf_low_detail") - row.prop(nif_obj_props, "sf_multiple_textures") - row.prop(nif_obj_props, "sf_non_projective_shadows") - row.prop(nif_obj_props, "sf_parallax_occulsion") - row.prop(nif_obj_props, "sf_parallax_shader_index_15") - row.prop(nif_obj_props, "sf_refraction") - row.prop(nif_obj_props, "sf_remappable_textures") - row.prop(nif_obj_props, "sf_shadow_frustum") - row.prop(nif_obj_props, "sf_shadow_map") - row.prop(nif_obj_props, "sf_single_pass") - row.prop(nif_obj_props, "sf_skinned") - row.prop(nif_obj_props, "sf_specular") - row.prop(nif_obj_props, "sf_tree_billboard") - row.prop(nif_obj_props, "sf_unknown_1") - row.prop(nif_obj_props, "sf_unknown_2") - row.prop(nif_obj_props, "sf_unknown_3") - row.prop(nif_obj_props, "sf_unknown_4") - row.prop(nif_obj_props, "sf_vertex_alpha") - row.prop(nif_obj_props, "sf_window_environment_mapping") - row.prop(nif_obj_props, "sf_z_buffer_test") + for property_name in sorted(NifFormat.BSShaderFlags._names): + row.prop(nif_obj_props, property_name) elif nif_obj_props.bs_shadertype in ('BSLightingShaderProperty', 'BSEffectShaderProperty'): row.prop(nif_obj_props, "bslsp_shaderobjtype") - row.prop(nif_obj_props, "slsf_1_cast_shadows") - row.prop(nif_obj_props, "slsf_1_decal") - row.prop(nif_obj_props, "slsf_1_dynamic_decal") - row.prop(nif_obj_props, "slsf_1_environment_mapping") - row.prop(nif_obj_props, "slsf_1_external_emittance") - row.prop(nif_obj_props, "slsf_1_eye_environment_mapping") - row.prop(nif_obj_props, "slsf_1_facegen_detail") - row.prop(nif_obj_props, "slsf_1_facegen_rgb_tint") - row.prop(nif_obj_props, "slsf_1_fire_refraction") - row.prop(nif_obj_props, "slsf_1_greyscale_to_palettealpha") - row.prop(nif_obj_props, "slsf_1_greyscale_to_palettecolor") - row.prop(nif_obj_props, "slsf_1_hair_soft_lighting") - row.prop(nif_obj_props, "slsf_1_Landscape") - row.prop(nif_obj_props, "slsf_1_localmap_hide_secret") - row.prop(nif_obj_props, "slsf_1_model_space_normals") - row.prop(nif_obj_props, "slsf_1_multiple_textures") - row.prop(nif_obj_props, "slsf_1_non_projective_shadows") - row.prop(nif_obj_props, "slsf_1_own_emit") - row.prop(nif_obj_props, "slsf_1_parallax_occlusion") - row.prop(nif_obj_props, "slsf_1_Parallax") - row.prop(nif_obj_props, "slsf_1_projected_uv") - row.prop(nif_obj_props, "slsf_1_recieve_shadows") - row.prop(nif_obj_props, "slsf_1_refraction") - row.prop(nif_obj_props, "slsf_1_remappable_textures") - row.prop(nif_obj_props, "slsf_1_screendoor_alpha_fade") - row.prop(nif_obj_props, "slsf_1_skinned") - row.prop(nif_obj_props, "slsf_1_soft_effect") - row.prop(nif_obj_props, "slsf_1_specular") - row.prop(nif_obj_props, "slsf_1_temp_refraction") - row.prop(nif_obj_props, "slsf_1_use_falloff") - row.prop(nif_obj_props, "slsf_1_vertex_alpha") - row.prop(nif_obj_props, "slsf_1_z_buffer_test") - row.prop(nif_obj_props, "slsf_2_anisotropic_lighting") - row.prop(nif_obj_props, "slsf_2_assume_shadowmask") - row.prop(nif_obj_props, "slsf_2_back_lighting") - row.prop(nif_obj_props, "slsf_2_billboard") - row.prop(nif_obj_props, "slsf_2_cloud_lod") - row.prop(nif_obj_props, "slsf_2_double_sided") - row.prop(nif_obj_props, "slsf_2_effect_lighting") - row.prop(nif_obj_props, "slsf_2_env_map_light_fade") - row.prop(nif_obj_props, "slsf_2_fit_slope") - row.prop(nif_obj_props, "slsf_2_glow_map") - row.prop(nif_obj_props, "slsf_2_hd_lod_objects") - row.prop(nif_obj_props, "slsf_2_hide_on_local_map") - row.prop(nif_obj_props, "slsf_2_lod_landscape") - row.prop(nif_obj_props, "slsf_2_lod_objects") - row.prop(nif_obj_props, "slsf_2_multi_index_snow") - row.prop(nif_obj_props, "slsf_2_multi_layer_parallax") - row.prop(nif_obj_props, "slsf_2_no_fade") - row.prop(nif_obj_props, "slsf_2_no_lod_land_blend") - row.prop(nif_obj_props, "slsf_2_no_transparency_multisampling") - row.prop(nif_obj_props, "slsf_2_packed_tangent") - row.prop(nif_obj_props, "slsf_2_premult_alpha") - row.prop(nif_obj_props, "slsf_2_rim_lighting") - row.prop(nif_obj_props, "slsf_2_soft_lighting") - row.prop(nif_obj_props, "slsf_2_tree_anim") - row.prop(nif_obj_props, "slsf_2_uniform_scale") - row.prop(nif_obj_props, "slsf_2_unused01") - row.prop(nif_obj_props, "slsf_2_unused02") - row.prop(nif_obj_props, "slsf_2_vertex_colors") - row.prop(nif_obj_props, "slsf_2_vertex_lighting") - row.prop(nif_obj_props, "slsf_2_weapon_blood") - row.prop(nif_obj_props, "slsf_2_wireframe") - row.prop(nif_obj_props, "slsf_2_z_buffer_write") + for property_name in sorted(NifFormat.SkyrimShaderPropertyFlags1._names): + row.prop(nif_obj_props, property_name) + for property_name in sorted(NifFormat.SkyrimShaderPropertyFlags2._names): + row.prop(nif_obj_props, property_name) classes = [ diff --git a/io_scene_niftools/utils/math.py b/io_scene_niftools/utils/math.py index f2dd5f0b2..7194c106c 100644 --- a/io_scene_niftools/utils/math.py +++ b/io_scene_niftools/utils/math.py @@ -82,15 +82,19 @@ def export_keymat(rest_rot, key_matrix, bone): def get_bind_matrix(bone): """Get a nif armature-space matrix from a blender bone. """ - bind = correction @ correction_inv @ bone.matrix_local @ correction + bind = bone.matrix_local @ correction if bone.parent: - p_bind_restored = correction @ correction_inv @ bone.parent.matrix_local @ correction + p_bind_restored = bone.parent.matrix_local @ correction bind = p_bind_restored.inverted() @ bind return bind +def blender_bind_to_nif_bind(blender_armature_space_matrix): + return blender_armature_space_matrix @ correction + + def nif_bind_to_blender_bind(nif_armature_space_matrix): - return correction_inv @ correction @ nif_armature_space_matrix @ correction_inv + return nif_armature_space_matrix @ correction_inv def import_matrix(n_block, relative_to=None):