diff --git a/.gitignore b/.gitignore index c668405a..1779ade7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ var/ *.egg-info/ .installed.cfg *.egg +.vs/ # PyInstaller # Usually these files are written by a python script from a template diff --git a/addon/io_scs_tools/__init__.py b/addon/io_scs_tools/__init__.py index e0df0a65..4934c1f7 100644 --- a/addon/io_scs_tools/__init__.py +++ b/addon/io_scs_tools/__init__.py @@ -21,9 +21,9 @@ bl_info = { "name": "SCS Tools", "description": "Setup models, Import-Export SCS data format", - "author": "Simon Lusenc (50keda), Milos Zajic (4museman)", - "version": (2, 4, "aeadde03"), - "blender": (3, 2, 0), + "author": "Simon Lusenc (50keda), Milos Zajic (4museman), Michal (Michaleczeq)", + "version": (2, 4, "aeadde03", 8, 1), + "blender": (5, 0, 0), "location": "File > Import-Export", "doc_url": "http://modding.scssoft.com/wiki/Documentation/Tools/SCS_Blender_Tools", "tracker_url": "http://forum.scssoft.com/viewforum.php?f=163", @@ -222,6 +222,11 @@ class SCS_TOOLS_OT_Export(bpy.types.Operator, _SCSExportHelper, ExportHelper): filename_ext = ".pim" filter_glob: StringProperty(default=str("*" + filename_ext), options={'HIDDEN'}) + def __init__(self, *args, **kwargs): + bpy.types.Operator.__init__(self, *args, **kwargs) + _SCSExportHelper.__init__(self, *args, **kwargs) + ExportHelper.__init__(self) + def execute(self, context): # convert filepath to None if empty, so export will ignore given menu file path and try to export to other none menu set paths if self.filepath == "": diff --git a/addon/io_scs_tools/consts.py b/addon/io_scs_tools/consts.py index 0ee4826a..8cc8f21c 100644 --- a/addon/io_scs_tools/consts.py +++ b/addon/io_scs_tools/consts.py @@ -194,7 +194,8 @@ class VehicleSides(Enum): FrontRight = 1 RearLeft = 2 RearRight = 3 - Middle = 4 + MiddleLeft = 4 + MiddleRight = 5 class VehicleLampTypes(Enum): """Defined lamp types for vehicles. @@ -227,6 +228,15 @@ class TrafficLightTypes(Enum): Yellow = 1 Green = 2 +class InteriorWindowTools: + """Constants related to interior window tools + """ + + class GlassReflection(Enum): + """Defined states of glass reflection. + """ + Enable = 0 + Disable = 1 class VertexColorTools: """Constants related to vertex color tools @@ -267,6 +277,7 @@ class Mesh: default_uv = "UV" default_vcol = "Col" + default_vfactor = "Factor" class PrefabLocators: @@ -444,6 +455,49 @@ class PIF: TYPE_END = 0x00020000 TYPE_CROSS_SHARP = 0x00040000 + class PSPCF: + """Constants represetning spawn point custom flags. + """ + # Depot Types + DEPOT_TYPE_MASK = 0x00000004 + DEPOT_TYPE_UNLOAD = 0x00000000 + DEPOT_TYPE_LOAD = 0x00000004 + + # Difficulties + DIFFICULTY_MASK = 0x00000003 + DIFFICULTY_NONE = 0x00000000 + DIFFICULTY_EASY = 0x00000001 + DIFFICULTY_MEDIUM = 0x00000002 + DIFFICULTY_HARD = 0x00000003 + + # Lenght + LENGHT_MASK = 0x000000F0 + LENGHT_14 = 0x00000000 + LENGHT_15 = 0x00000010 + LENGHT_16 = 0x00000020 + LENGHT_17 = 0x00000030 + LENGHT_18 = 0x00000040 + LENGHT_19 = 0x00000050 + LENGHT_20 = 0x00000060 + LENGHT_21 = 0x00000070 + LENGHT_22 = 0x00000080 + LENGHT_23 = 0x00000090 + LENGHT_24 = 0x000000A0 + LENGHT_25 = 0x000000B0 + LENGHT_26 = 0x000000C0 + LENGHT_27 = 0x000000D0 + LENGHT_28 = 0x000000E0 + UNLIMITED = 0x000000F0 + + # Rule + TRAILER_MASK = 0x000F0000 + TRAILER_ANY = 0x00000000 + TRAILER_BOX = 0x00010000 + TRAILER_TANK = 0x00020000 + TRAILER_DUMP_BULK = 0x00030000 + TRAILER_PLATFORM_LOG_CONT = 0x00040000 + TRAILER_LIVESTOCK = 0x00050000 + TRAILER_LOG = 0x00060000 class Bones: init_scale_key = "scs_init_scale" diff --git a/addon/io_scs_tools/exp/pia.py b/addon/io_scs_tools/exp/pia.py index 93b83e9c..1fd6689c 100644 --- a/addon/io_scs_tools/exp/pia.py +++ b/addon/io_scs_tools/exp/pia.py @@ -21,6 +21,7 @@ import os import bpy +from bpy_extras import anim_utils from collections import OrderedDict from mathutils import Vector, Matrix, Euler, Quaternion from io_scs_tools.utils import convert as _convert_utils @@ -31,7 +32,7 @@ from io_scs_tools.internals.containers import pix as _pix_container -def _get_custom_channels(scs_animation, action): +def _get_custom_channels(armature, scs_animation, action): custom_channels = [] frame_start = scs_animation.anim_start frame_end = scs_animation.anim_end @@ -40,8 +41,11 @@ def _get_custom_channels(scs_animation, action): loc_curves = {} # dictionary for storing "location" curves of action + action_slot = armature.animation_data.action_slot + channelbag = anim_utils.action_get_channelbag_for_slot(action, action_slot) + # get curves which are related to moving of armature object - for fcurve in action.fcurves: + for fcurve in channelbag.fcurves: if fcurve.data_path == 'location': loc_curves[fcurve.array_index] = fcurve @@ -103,7 +107,10 @@ def _get_bone_channels(scs_root_obj, armature, scs_animation, action, export_sca curves_per_bone = OrderedDict() # store all the curves we are interested in per bone names for bone in armature.data.bones: - for fcurve in action.fcurves: + action_slot = armature.animation_data.action_slot + channelbag = anim_utils.action_get_channelbag_for_slot(action, action_slot) + + for fcurve in channelbag.fcurves: # check if curve belongs to bone if '["' + bone.name + '"]' in fcurve.data_path: @@ -331,7 +338,7 @@ def export(scs_root_obj, armature, scs_animation, dirpath, name_suffix, skeleton total_time = scs_animation.length action = bpy.data.actions[scs_animation.action] bone_channels = _get_bone_channels(scs_root_obj, armature, scs_animation, action, scs_globals.export_scale) - custom_channels = _get_custom_channels(scs_animation, action) + custom_channels = _get_custom_channels(armature, scs_animation, action) # DATA CREATION header_section = _fill_header_section(scs_animation.name, scs_globals.export_write_signature) diff --git a/addon/io_scs_tools/exp/pim/exporter.py b/addon/io_scs_tools/exp/pim/exporter.py index fb7f5acc..8e85616c 100644 --- a/addon/io_scs_tools/exp/pim/exporter.py +++ b/addon/io_scs_tools/exp/pim/exporter.py @@ -204,6 +204,7 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat missing_uv_layers = {} # stores missing uvs specified by materials of this object missing_vcolor = False # indicates if object is missing vertex color layer missing_vcolor_a = False # indicates if object is missing vertex color alpha layer + missing_vfcolor = False # indicates if object is missing vertex color factor layer missing_skinned_verts = set() # indicates if object is having only partial skin, which is not allowed in our models has_unnormalized_skin = False # indicates if object has vertices which bones weight sum is smaller then one last_tangents_uv_layer = None # stores uv layer for which tangents were calculated, so tangents won't be calculated all over again @@ -425,23 +426,46 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat vcol += (alpha * 2,) - # 5. tangent -> loop.tangent; loop.bitangent_sign -> calc_tangents() has to be called before + # 5. vfcol -> vfcol_lay = mesh.color_attributes[2].data; vfcol_lay[loop_i].color + vfcol = None + vfactor_shader = ("piko.alldir") in material.scs_props.mat_effect_name + if vfactor_shader: + if _MESH_consts.default_vfactor not in mesh.color_attributes: # get FACTOR component + vfcol = (1.0,) * 4 + missing_vfcolor = True + else: + vfcolors = mesh.color_attributes[_MESH_consts.default_vfactor] + + if vfcolors.domain == 'POINT': + color = Color(vfcolors.data[vert_i].color[:3]) + alpha = vfcolors.data[vert_i].color[3] + elif vfcolors.domain == 'CORNER': + color = Color(vfcolors.data[loop_i].color[:3]) + alpha = vfcolors.data[loop_i].color[3] + else: + raise TypeError("Invalid vertex color domain type!") + + color = color.from_scene_linear_to_srgb() + + vfcol = tuple(round(value * 255) / 1.0 for value in (*color, alpha)) + + # 6. tangent -> loop.tangent; loop.bitangent_sign -> calc_tangents() has to be called before if pim_materials[pim_mat_name].get_nmap_uv_name(): # calculate tangents only if needed tangent = (tangent_transf_mat @ loop.tangent).normalized() tangent = (tangent[0], tangent[1], tangent[2], loop.bitangent_sign) else: tangent = None - # 6. There we go, vertex data collected! Now create internal vertex index, for triangle and skin stream construction + # 7. There we go, vertex data collected! Now create internal vertex index, for triangle and skin stream construction # Construct unique vertex index - donated by mesh and vertex index, as we may export more mesh objects into same piece, # thus only vertex index wouldn't be unique representation. unique_vert_i = "%i|%i" % (mesh_i, vert_i) - piece_vert_index = mesh_piece.add_vertex(unique_vert_i, position, normal, uvs, uvs_aliases, vcol, tangent) + piece_vert_index = mesh_piece.add_vertex(unique_vert_i, position, normal, uvs, uvs_aliases, vcol, vfcol, tangent) - # 7. Add vertex to triangle creation list + # 8. Add vertex to triangle creation list triangle_pvert_indices.append(piece_vert_index) - # 8. Get skinning data for vertex and save it to skin stream + # 9. Get skinning data for vertex and save it to skin stream if is_skin_used: bone_weights = {} bone_weights_sum = 0 @@ -463,7 +487,7 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat if bone_weights_sum < 1: has_unnormalized_skin = True - # 9. Terrain Points: save vertex to terrain points storage, if present in correct vertex group + # 10. Terrain Points: save vertex to terrain points storage, if present in correct vertex group if has_terrain_points: for group in mesh.vertices[vert_i].groups: @@ -526,6 +550,9 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat if missing_vcolor_a: lprint("W Object %r is missing vertex color alpha layer with name %r! Default alpha will be exported (0.5)", (mesh_obj.name, _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix)) + if missing_vfcolor: + lprint("W Object %r is missing vertex factor color layer with name %r! Default factor will be exported (0.0)", + (mesh_obj.name, _MESH_consts.default_vfactor)) if len(missing_skinned_verts) > 0: lprint("E Object %r from SCS Root %r has %s vertices which are not skinned to any bone, expect errors during conversion!", (mesh_obj.name, root_object.name, len(missing_skinned_verts))) diff --git a/addon/io_scs_tools/exp/pim/material.py b/addon/io_scs_tools/exp/pim/material.py index 6cb9f680..3a7a1f04 100644 --- a/addon/io_scs_tools/exp/pim/material.py +++ b/addon/io_scs_tools/exp/pim/material.py @@ -101,6 +101,26 @@ def __init__(self, index, alias, effect, blend_mat): self.__used_textures_without_uv_count += 1 + if blend_mat and "scs_shader_attributes" in blend_mat and "mappings" in blend_mat["scs_shader_attributes"]: + for tex_entry in blend_mat["scs_shader_attributes"]["mappings"].values(): + self.__used_textures_count += 1 + if "Tag" in tex_entry: + tex_type = tex_entry["Tag"] + mappings = getattr(blend_mat.scs_props, "shader_mapping_" + tex_type, []) + + for uv_map_i, uv_map in enumerate(mappings): + if uv_map.value != "": # filter out none specified mappings + + tex_coord_map[uv_map.tex_coord] = uv_map.value + + elif uv_map.tex_coord != -1: # if tex coord is -1 texture doesn't use uvs + lprint("W Mapping type '%s' on material '%s' is missing UV mapping value, expect problems in game!", + (tex_type, blend_mat.name)) + + else: # if texture doesn't have mappings it means uv is not required for it + + self.__used_textures_without_uv_count += 1 + # create uv layer map with used tex_coord on it (this tex_coords now represents aliases for given uv layers) # It also uses ordered dictionary because order of keys now defines actually physical order for uvs in PIM file self.__uvs_map_by_name = OrderedDict() diff --git a/addon/io_scs_tools/exp/pim/piece.py b/addon/io_scs_tools/exp/pim/piece.py index 30653a24..62244ee7 100644 --- a/addon/io_scs_tools/exp/pim/piece.py +++ b/addon/io_scs_tools/exp/pim/piece.py @@ -59,8 +59,8 @@ def get_global_triangle_count(): return Piece.__global_triangle_count @staticmethod - def __calc_vertex_hash(index, normal, uvs, rgba, tangent): - """Calculates vertex hash from original vertex index, uvs components and vertex color. + def __calc_vertex_hash(index, normal, uvs, rgba, factor, tangent): + """Calculates vertex hash from original vertex index, uvs components, vertex color and vertex factor color. :param index: original index from Blender mesh :type index: str :param normal: normalized vector representation of the normal @@ -69,6 +69,8 @@ def __calc_vertex_hash(index, normal, uvs, rgba, tangent): :type uvs: list of (tuple | mathutils.Vector) :param rgba: rgba representation of vertex color in SCS values :type rgba: tuple | mathutils.Color + :param factor: rgba representation of vertex factor color in SCS values + :type factor: tuple | mathutils.Color :param tangent: vertex tangent in SCS coordinates or none, if piece doesn't have tangents :type tangent: tuple | None :return: calculated vertex hash @@ -99,6 +101,12 @@ def __calc_vertex_hash(index, normal, uvs, rgba, tangent): int(rgba[2] * fprec), int(rgba[3] * fprec)) + if factor: + vertex_hash += (int(factor[0] * fprec), + int(factor[1] * fprec), + int(factor[2] * fprec), + int(factor[3] * fprec)) + for uv in uvs: vertex_hash += (int(uv[0] * fprec), int(uv[1] * fprec)) @@ -155,7 +163,7 @@ def add_triangle(self, triangle): return True - def add_vertex(self, vert_index, position, normal, uvs, uvs_aliases, rgba, tangent): + def add_vertex(self, vert_index, position, normal, uvs, uvs_aliases, rgba, factor, tangent): """Adds new vertex to position and normal streams :param vert_index: original vertex index from Blender mesh :type vert_index: str @@ -169,13 +177,15 @@ def add_vertex(self, vert_index, position, normal, uvs, uvs_aliases, rgba, tange :type uvs_aliases: list[list[str]] :param rgba: rgba representation of vertex color in SCS values :type rgba: tuple | mathutils.Color + :param factor: rgba representation of vertex factor color in SCS values + :type factor: tuple | mathutils.Color :param tangent: tuple representation of vertex tangent in SCS values or None if piece doesn't have tangents :type tangent: tuple | None :return: vertex index inside piece streams ( use it for adding triangles ) :rtype: int """ - vertex_hash = self.__calc_vertex_hash(vert_index, normal, uvs, rgba, tangent) + vertex_hash = self.__calc_vertex_hash(vert_index, normal, uvs, rgba, factor, tangent) # save vertex if the vertex with the same properties doesn't exists yet in streams if vertex_hash not in self.__vertices_hash: @@ -213,6 +223,14 @@ def add_vertex(self, vert_index, position, normal, uvs, uvs_aliases, rgba, tange stream = self.__streams[Stream.Types.RGBA] stream.add_entry(rgba) + if factor: + # create factor stream on demand + if Stream.Types.FACTOR not in self.__streams: + self.__streams[Stream.Types.FACTOR] = Stream(Stream.Types.FACTOR, -1) + + stream = self.__streams[Stream.Types.FACTOR] + stream.add_entry(factor) + vert_index_internal = stream.get_size() - 1 # streams has to be alligned so I can take last one for the index self.__vertices_hash[vertex_hash] = vert_index_internal diff --git a/addon/io_scs_tools/exp/pim/piece_stream.py b/addon/io_scs_tools/exp/pim/piece_stream.py index 3336a0b7..ee9a49d7 100644 --- a/addon/io_scs_tools/exp/pim/piece_stream.py +++ b/addon/io_scs_tools/exp/pim/piece_stream.py @@ -31,6 +31,7 @@ class Types: TANGENT = "_TANGENT" RGB = "_RGB" RGBA = "_RGBA" + FACTOR = "_FACTOR" # NOTE: used only in piko.alldir flavor UV = "_UV" # NOTE: there can be up to 9 uv streams TUV = "_TUV" # NOTE: there can be up to 9 tuv streams @@ -66,6 +67,8 @@ def __init__(self, stream_type, index): self.__format = "FLOAT3" elif stream_type == Stream.Types.RGBA: self.__format = "FLOAT4" + elif stream_type == Stream.Types.FACTOR: + self.__format = "FLOAT4" elif stream_type == Stream.Types.UV: self.__tag_index = index self.__format = "FLOAT2" @@ -93,6 +96,8 @@ def add_entry(self, value): # return False # if self.__tag == Stream.Types.RGBA and len(value) != 4: # return False + # if self.__tag == Stream.Types.FACTOR and len(value) != 4: + # return False # if self.__tag == Stream.Types.UV and len(value) != 2: # return False diff --git a/addon/io_scs_tools/exp/pim_ef/exporter.py b/addon/io_scs_tools/exp/pim_ef/exporter.py index f1541353..ab3b9594 100644 --- a/addon/io_scs_tools/exp/pim_ef/exporter.py +++ b/addon/io_scs_tools/exp/pim_ef/exporter.py @@ -177,6 +177,7 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat missing_uv_layers = {} # stores missing uvs specified by materials of this object missing_vcolor = False # indicates if object is missing vertex color layer missing_vcolor_a = False # indicates if object is missing vertex color alpha layer + missing_vfcolor = False # indicates if object is missing vertex factor color layer missing_skinned_verts = set() # indicates if object is having only partial skin, which is not allowed in our models has_unnormalized_skin = False # indicates if object has vertices which bones weight sum is smaller then one @@ -220,6 +221,8 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat uvs_names = collections.OrderedDict() vert_rgbas = [] rgbas_names = collections.OrderedDict() + vert_factors = [] + factors_names = collections.OrderedDict() tex_coord_alias_map = pim_materials[pim_mat_name].get_tex_coord_map() for loop_i in poly.loop_indices: @@ -312,11 +315,40 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat rgbas.append(vcol) rgbas_names[_MESH_consts.default_vcol] = True + # 5. vfcol -> vfcol_lay = mesh.color_attributes[2].data; vfcol_lay[loop_i].color + factors = [] + vfactor_shader = ("piko.alldir") in material.scs_props.mat_effect_name + if vfactor_shader: + if _MESH_consts.default_vfactor not in mesh.color_attributes: # get FACTOR component + vfcol = (1.0,) * 4 + missing_vfcolor = True + else: + vfcolors = mesh.color_attributes[_MESH_consts.default_vfactor] + + if vfcolors.domain == 'POINT': + color = Color(vfcolors.data[vert_i].color[:3]) + alpha = vfcolors.data[vert_i].color[3] + elif vfcolors.domain == 'CORNER': + color = Color(vfcolors.data[loop_i].color[:3]) + alpha = vfcolors.data[loop_i].color[3] + else: + raise TypeError("Invalid vertex color domain type!") + + color = color.from_scene_linear_to_srgb() + + # round and convert from 0.0-1.0 to 0-255 range, then make it float again + vfcol = tuple(round(value * 255) / 1.0 for value in (*color, alpha)) + + factors.append(vfcol) + factors_names[_MESH_consts.default_vfactor] = True + + vert_factors.append(factors) + # export rest of the vertex colors too (also multiply with 2 and with vcol multiplicator) for vcol_layer in mesh.color_attributes: # we already computed thoose so ignore them - if vcol_layer.name in [_MESH_consts.default_vcol, _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix]: + if vcol_layer.name in [_MESH_consts.default_vcol, _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix, _MESH_consts.default_vfactor]: continue color = vcol_layer.data[loop_i].color @@ -402,7 +434,9 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat list(uvs_names.keys()), uvs_aliases, tuple(vert_rgbas[::winding_order]), - list(rgbas_names.keys()) + list(rgbas_names.keys()), + tuple(vert_factors[::winding_order]), + list(factors_names.keys()) ) # as we captured all hard edges collect them now and put it into Piece @@ -437,6 +471,9 @@ def execute(dirpath, name_suffix, root_object, armature_object, skeleton_filepat if missing_vcolor_a: lprint("W Object %r is missing vertex color alpha layer with name %r! Default alpha will be exported (0.5)", (mesh_obj.name, _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix)) + if missing_vfcolor: + lprint("W Object %r is missing vertex factor color layer with name %r! Default RGBA color will be exported (0.0, 0.0, 0.0, 0.0)!", + (mesh_obj.name, _MESH_consts.default_vfactor)) if len(missing_skinned_verts) > 0: lprint("E Object %r from SCS Root %r has %s vertices which are not skinned to any bone, expect errors during conversion!", (mesh_obj.name, root_object.name, len(missing_skinned_verts))) diff --git a/addon/io_scs_tools/exp/pim_ef/piece.py b/addon/io_scs_tools/exp/pim_ef/piece.py index f4695825..bbe8f27c 100644 --- a/addon/io_scs_tools/exp/pim_ef/piece.py +++ b/addon/io_scs_tools/exp/pim_ef/piece.py @@ -62,7 +62,7 @@ def get_global_face_count(): @staticmethod def __calc_vertex_hash(index, position): - """Calculates vertex hash from original vertex index, uvs components and vertex color. + """Calculates vertex hash from original vertex index, uvs components, vertex color and vertex factor color. :param index: original index from Blender mesh :type index: int :param position: vector of 3 floats presenting vertex position in SCS values @@ -104,7 +104,7 @@ def __init__(self, index): Piece.__global_piece_count += 1 - def add_face(self, material, vert_indicies, vert_normals, vert_uvs, uvs_names, uvs_aliases, vert_rgbas, rgbas_names): + def add_face(self, material, vert_indicies, vert_normals, vert_uvs, uvs_names, uvs_aliases, vert_rgbas, rgbas_names, vert_factors, factors_names): """Adds new face to piece. :param material: material that should be used on for this piece @@ -123,6 +123,10 @@ def add_face(self, material, vert_indicies, vert_normals, vert_uvs, uvs_names, u :type vert_rgbas: list[tuple | mathutils.Color] | tuple[tuple | mathutils.Color] :param rgbas_names: tuple or list of vertex color layer names used on vertex :type rgbas_names: list[str] | tuple[str] + :param vert_factors: list of factors vertex colors in SCS values + :type vert_factors: list[tuple | mathutils.Color] | tuple[tuple | mathutils.Color] + :param factors_names: tuple or list of vertex factor color layer names used on vertex + :type factors_names: list[str] | tuple[str] :return: True if added; False otherwise :rtype: bool """ @@ -136,8 +140,12 @@ def add_face(self, material, vert_indicies, vert_normals, vert_uvs, uvs_names, u return False # check integrity between all parameters - if not len(vert_indicies) == len(vert_normals) == len(vert_uvs) == len(vert_rgbas): - return False + if vert_factors: + if not len(vert_indicies) == len(vert_normals) == len(vert_uvs) == len(vert_rgbas) == len(vert_factors): + return False + else: + if not len(vert_indicies) == len(vert_normals) == len(vert_uvs) == len(vert_rgbas): + return False face = Face(len(self.__faces), material, vert_indicies) @@ -152,6 +160,10 @@ def add_face(self, material, vert_indicies, vert_normals, vert_uvs, uvs_names, u # add rgbas per vertex face.add_rgbas(vert_rgbas[i], rgbas_names) + # add factors per vertex if available + if vert_factors: + face.add_factors(vert_factors[i], factors_names) + self.__faces.append(face) Piece.__global_face_count += 1 diff --git a/addon/io_scs_tools/exp/pim_ef/piece_face.py b/addon/io_scs_tools/exp/pim_ef/piece_face.py index 4b455bcb..670e535a 100644 --- a/addon/io_scs_tools/exp/pim_ef/piece_face.py +++ b/addon/io_scs_tools/exp/pim_ef/piece_face.py @@ -116,6 +116,29 @@ def add_rgbas(self, rgbas, rgbas_names): """:type: Stream""" stream.add_entry(rgba) + def add_factors(self, factors, factors_names): + """Adds next vertex factor colors to the end of the stream. + + NOTE: There is no check between length of stream and number of indicies present in face + + :param factors: next vertex vertex factor colors + :type factors: tuple[tuple[float]] | tuple[mathutils.Vector] + :param factors_names: tuple or list of uv vertex factor color layer names used on vertex + :type factors_names: list[str] | tuple[str] + """ + + for i, factor in enumerate(factors): + + stream_type = Stream.Types.FACTOR + vfcol_type = Stream.Types.FACTOR + str(i) + + if vfcol_type not in self.__streams: + self.__streams[vfcol_type] = Stream(stream_type, i, factors_names[i]) + + stream = self.__streams[vfcol_type] + """:type: Stream""" + stream.add_entry(factor) + def get_stream_count(self): """Gets count of all streams used in this face. diff --git a/addon/io_scs_tools/exp/pip/exporter.py b/addon/io_scs_tools/exp/pip/exporter.py index 8f5a785d..e03ac9ec 100644 --- a/addon/io_scs_tools/exp/pip/exporter.py +++ b/addon/io_scs_tools/exp/pip/exporter.py @@ -327,6 +327,8 @@ def execute(dirpath, filename, name_suffix, prefab_locator_list, offset_matrix, spawn_point.set_type(int(locator_scs_props.locator_prefab_spawn_type)) + spawn_point.set_flags(locator_scs_props) + pip_spawn_points.append(spawn_point) # semaphores creation diff --git a/addon/io_scs_tools/exp/pip/spawn_point.py b/addon/io_scs_tools/exp/pip/spawn_point.py index 095a6758..b1f4dbfe 100644 --- a/addon/io_scs_tools/exp/pip/spawn_point.py +++ b/addon/io_scs_tools/exp/pip/spawn_point.py @@ -44,6 +44,7 @@ def __init__(self, name): self.__position = (0.,) * 3 self.__rotation = (1.,) + (0.,) * 3 self.__type = 0 # NONE + self.__flags = 0 # NONE SpawnPoint.__global_spawn_point_counter += 1 @@ -74,6 +75,28 @@ def set_type(self, sp_type): """ self.__type = sp_type + def set_flags(self, sp_flags): + """Set flags of spawn point. + + NOTE: there is no safety check if value is valid, + make sure that prefab locator properties are synced with PSPCF_* consts. + + :param sp_flags: integer flags of spawn point + :type sp_flags: int + """ + + # depot type + self.__flags |= int(sp_flags.locator_prefab_custom_depot_type) + + # parking difficulty + self.__flags |= int(sp_flags.locator_prefab_custom_parking_difficulty) + + # lenght + self.__flags |= int(sp_flags.locator_prefab_custom_lenght) + + # rule + self.__flags |= int(sp_flags.locator_prefab_custom_rule) + def get_as_section(self): """Get spawn point information represented with SectionData structure class. @@ -86,5 +109,6 @@ def get_as_section(self): section.props.append(("Position", ["&&", tuple(self.__position)])) section.props.append(("Rotation", ["&&", tuple(self.__rotation)])) section.props.append(("Type", self.__type)) + section.props.append(("Flags", self.__flags)) return section diff --git a/addon/io_scs_tools/exp/pit.py b/addon/io_scs_tools/exp/pit.py index d153e926..233abd64 100644 --- a/addon/io_scs_tools/exp/pit.py +++ b/addon/io_scs_tools/exp/pit.py @@ -235,9 +235,14 @@ def get_texture_path_from_material(material, texture_type, export_path): return "" else: - lprint("E Texture file %r from material %r doesn't exists inside current Project Base Path.\n\t " + + if texture_raw_path: + lprint("E Texture file %r from material %r doesn't exists inside current Project Base Path.\n\t " + "TOBJ won't be exported and reference will remain empty, expect problems!", (texture_raw_path, material.name)) + else: + lprint("E Texture type %r on material %r is missing texture.\n\t " + + "TOBJ won't be exported and reference will remain empty, expect problems!", + (texture_type[8:], material.name)) return "" # CREATE TOBJ FILE diff --git a/addon/io_scs_tools/imp/pia.py b/addon/io_scs_tools/imp/pia.py index 5adb334f..41185120 100644 --- a/addon/io_scs_tools/imp/pia.py +++ b/addon/io_scs_tools/imp/pia.py @@ -124,44 +124,44 @@ def _get_anim_channels(pia_container, section_name="BoneChannel"): return channels -def _create_fcurves(anim_action, anim_group, anim_curve, rot_euler=True, types='LocRotSca'): - """Creates animation curves for provided Action / Group (Bone). +def _create_fcurves(channelbag, anim_group, anim_curve, rot_euler=True, types='LocRotSca'): + """Creates animation curves for provided Channelbag / Group (Bone). :return: Tuple of position vector, rotation quaternion and scaling vector :rtype (fcurve, fcurve, fcurve) """ pos_fcurves = rot_fcurves = sca_fcurves = None if 'Loc' in types: - fcurve_pos_x = anim_action.fcurves.new(str(anim_curve + '.location'), index=0) - fcurve_pos_y = anim_action.fcurves.new(str(anim_curve + '.location'), index=1) - fcurve_pos_z = anim_action.fcurves.new(str(anim_curve + '.location'), index=2) + fcurve_pos_x = channelbag.fcurves.new(str(anim_curve + '.location'), index=0) + fcurve_pos_y = channelbag.fcurves.new(str(anim_curve + '.location'), index=1) + fcurve_pos_z = channelbag.fcurves.new(str(anim_curve + '.location'), index=2) fcurve_pos_x.group = anim_group fcurve_pos_y.group = anim_group fcurve_pos_z.group = anim_group pos_fcurves = (fcurve_pos_x, fcurve_pos_y, fcurve_pos_z) if 'Rot' in types: if rot_euler: - fcurve_rot_x = anim_action.fcurves.new(str(anim_curve + '.rotation_euler'), index=0) - fcurve_rot_y = anim_action.fcurves.new(str(anim_curve + '.rotation_euler'), index=1) - fcurve_rot_z = anim_action.fcurves.new(str(anim_curve + '.rotation_euler'), index=2) + fcurve_rot_x = channelbag.fcurves.new(str(anim_curve + '.rotation_euler'), index=0) + fcurve_rot_y = channelbag.fcurves.new(str(anim_curve + '.rotation_euler'), index=1) + fcurve_rot_z = channelbag.fcurves.new(str(anim_curve + '.rotation_euler'), index=2) fcurve_rot_x.group = anim_group fcurve_rot_y.group = anim_group fcurve_rot_z.group = anim_group rot_fcurves = (fcurve_rot_x, fcurve_rot_y, fcurve_rot_z) else: - fcurve_rot_w = anim_action.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=0) - fcurve_rot_x = anim_action.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=1) - fcurve_rot_y = anim_action.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=2) - fcurve_rot_z = anim_action.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=3) + fcurve_rot_w = channelbag.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=0) + fcurve_rot_x = channelbag.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=1) + fcurve_rot_y = channelbag.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=2) + fcurve_rot_z = channelbag.fcurves.new(str(anim_curve + '.rotation_quaternion'), index=3) fcurve_rot_w.group = anim_group fcurve_rot_x.group = anim_group fcurve_rot_y.group = anim_group fcurve_rot_z.group = anim_group rot_fcurves = (fcurve_rot_w, fcurve_rot_x, fcurve_rot_y, fcurve_rot_z) if 'Sca' in types: - fcurve_sca_x = anim_action.fcurves.new(str(anim_curve + '.scale'), index=0) - fcurve_sca_y = anim_action.fcurves.new(str(anim_curve + '.scale'), index=1) - fcurve_sca_z = anim_action.fcurves.new(str(anim_curve + '.scale'), index=2) + fcurve_sca_x = channelbag.fcurves.new(str(anim_curve + '.scale'), index=0) + fcurve_sca_y = channelbag.fcurves.new(str(anim_curve + '.scale'), index=1) + fcurve_sca_z = channelbag.fcurves.new(str(anim_curve + '.scale'), index=2) fcurve_sca_x.group = anim_group fcurve_sca_y.group = anim_group fcurve_sca_z.group = anim_group @@ -259,8 +259,16 @@ def load(root_object, pia_files, armature, pis_filepath=None, bones=None): # CREATE ANIMATION ACTIONS anim_action = bpy.data.actions.new(animation_name + "_action") anim_action.use_fake_user = True + + anim_slot = anim_action.slots.new(id_type='OBJECT', name="SCS Slot") + anim_layer = anim_action.layers.new("AnimLayer") + strip = anim_layer.strips.new(type='KEYFRAME') + channelbag = strip.channelbag(anim_slot, ensure=True) + anim_data = armature.animation_data if armature.animation_data else armature.animation_data_create() anim_data.action = anim_action + anim_data.action_slot = anim_action.slots[0] + # LOAD BONE CHANNELS bone_channels = _get_anim_channels(pia_container, section_name="BoneChannel") @@ -277,7 +285,7 @@ def load(root_object, pia_files, armature, pis_filepath=None, bones=None): streams = bone_channels[bone_name][2] # CREATE ANIMATION GROUP - anim_group = anim_action.groups.new(bone_name) + anim_group = channelbag.groups.new(bone_name) armature.pose.bones[bone_name].rotation_mode = 'XYZ' # Set rotation mode. # use pose bone scale set on PIS import @@ -288,7 +296,7 @@ def load(root_object, pia_files, armature, pis_filepath=None, bones=None): # CREATE FCURVES (pos_fcurves, rot_fcurves, - sca_fcurves) = _create_fcurves(anim_action, anim_group, str('pose.bones["' + bone_name + '"]'), rot_euler=True) + sca_fcurves) = _create_fcurves(channelbag, anim_group, str('pose.bones["' + bone_name + '"]'), rot_euler=True) # GET BONE REST POSITION MATRIX bone_rest_matrix_scs = bones[bone_name][1].transposed() @@ -368,19 +376,19 @@ def load(root_object, pia_files, armature, pis_filepath=None, bones=None): # print(' channel %r - streams %s - keyframes %s' % (channel_name, stream_count, keyframe_count)) # CREATE ANIMATION GROUP - # anim_group = anim_action.groups.new(channel_name) - anim_group = anim_action.groups.new('Location') + # anim_group = channelbag.groups.new(channel_name) + anim_group = channelbag.groups.new('Location') # armature.[channel_name].rotation_mode = 'XYZ' ## Set rotation mode. # active_bone = armature.data.bones[channel_name] # parent_bone = active_bone.parent # CREATE FCURVES - # pos_fcurves, rot_fcurves, sca_fcurves = _create_fcurves(anim_action, anim_group, anim_curve, rot_euler=True, + # pos_fcurves, rot_fcurves, sca_fcurves = _create_fcurves(channelbag, anim_group, anim_curve, rot_euler=True, # types='LocRotSca') - # pos_fcurves, rot_fcurves, sca_fcurves = _create_fcurves(anim_action, anim_group, anim_curve, types='Loc') - fcurve_pos_x = anim_action.fcurves.new('location', index=0) - fcurve_pos_y = anim_action.fcurves.new('location', index=1) - fcurve_pos_z = anim_action.fcurves.new('location', index=2) + # pos_fcurves, rot_fcurves, sca_fcurves = _create_fcurves(channelbag, anim_group, anim_curve, types='Loc') + fcurve_pos_x = channelbag.fcurves.new('location', index=0) + fcurve_pos_y = channelbag.fcurves.new('location', index=1) + fcurve_pos_z = channelbag.fcurves.new('location', index=2) fcurve_pos_x.group = anim_group fcurve_pos_y.group = anim_group fcurve_pos_z.group = anim_group diff --git a/addon/io_scs_tools/imp/pim.py b/addon/io_scs_tools/imp/pim.py index da3ed9ce..83a9a112 100644 --- a/addon/io_scs_tools/imp/pim.py +++ b/addon/io_scs_tools/imp/pim.py @@ -144,6 +144,7 @@ def _get_piece_streams(section): mesh_tangents = [] mesh_rgb = {} mesh_rgba = {} + mesh_factor = {} mesh_uv = {} mesh_scalars = [] mesh_tuv = [] @@ -197,6 +198,9 @@ def _get_piece_streams(section): elif stream_tag.startswith("_RGBA") and stream_format == 'FLOAT4': mesh_rgba[str(_MESH_consts.default_vcol)] = data_block # print('data_block.props: %s' % str(data_block)) + elif stream_tag.startswith("_FACTOR") and stream_format == 'FLOAT4': + mesh_factor[str(_MESH_consts.default_vfactor)] = data_block + # print('data_block.props: %s' % str(data_block)) elif stream_tag.startswith("_UV") and stream_format == 'FLOAT2': mesh_uv[str(_MESH_consts.default_uv + num_suffix)] = { "data": data_block, @@ -210,7 +214,7 @@ def _get_piece_streams(section): for data_line in sec.data: data_line.reverse() # flip triangle normals mesh_triangles.append(data_line) - return mesh_vertices, mesh_normals, mesh_tangents, mesh_rgb, mesh_rgba, mesh_scalars, mesh_uv, mesh_tuv, mesh_triangles + return mesh_vertices, mesh_normals, mesh_tangents, mesh_rgb, mesh_rgba, mesh_factor, mesh_scalars, mesh_uv, mesh_tuv, mesh_triangles def get_part_properties(section): @@ -391,6 +395,7 @@ def _create_piece( mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, object_skinning, mesh_uv, @@ -445,6 +450,11 @@ def _create_piece( else: mesh_rgb_final = [] + # VERTEX FACTORS + mesh_factor_final = [] + if mesh_factor: + mesh_factor_final = mesh_factor + vcolor_corrupt = False for vc_layer_name in mesh_rgb_final: @@ -461,6 +471,9 @@ def _create_piece( if vcolor_corrupt: lprint("W Piece %r has vertices with vertex color greater the 1.0, clamping it!", (name,)) + for vfc_layer_name in mesh_factor_final: + _mesh_utils.bm_make_vfc_layer(5, bm, vfc_layer_name, mesh_factor_final[vfc_layer_name]) + context.window_manager.progress_update(0.5) bm.to_mesh(mesh) @@ -475,12 +488,11 @@ def _create_piece( # NORMALS - has to be applied after bmesh creation as they are set directly to mesh if _get_scs_globals().import_use_normals: - mesh.create_normals_split() - # first set normals directly to loops + clnors = [] for loop in mesh.loops: curr_n = _convert_utils.scs_to_blend_matrix() @ Vector(mesh_normals[loop.vertex_index]) - loop.normal[:] = curr_n + clnors.extend(curr_n) # then we have to go trough very important step they say, # as without validation we get wrong result for some normals @@ -490,13 +502,8 @@ def _create_piece( mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) # finally fill clnors from loops normals and apply them (taken from official Blenders scripts) - clnors = array.array('f', [0.0] * (len(mesh.loops) * 3)) - mesh.loops.foreach_get("normal", clnors) mesh.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) - mesh.use_auto_smooth = True - mesh.auto_smooth_angle = 3.14 - mesh.free_normals_split() else: # set polygons to use smooth representation only mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) @@ -607,6 +614,7 @@ def _create_piece( mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, object_skinning, mesh_uv, @@ -645,10 +653,10 @@ def visualise_normals(name, transformed_mesh_vertices, mesh_normals, import_scal :return: """ if mesh_normals: - transformed_mesh_normals = [io_utils.change_to_scs_xyz_coordinates(vec, import_scale) for vec in mesh_normals] + transformed_mesh_normals = [_convert_utils.change_to_scs_xyz_coordinates(vec, import_scale) for vec in mesh_normals] mesh_norm_vizu = bpy.data.meshes.new(str(name + "_norm_vizu")) object_norm_vizu = bpy.data.objects.new(str(name + "_norm_vizu"), mesh_norm_vizu) - bpy.context.scene.objects.link(object_norm_vizu) + bpy.context.collection.objects.link(object_norm_vizu) mesh_norm_vizu.vertices.add(len(transformed_mesh_vertices) * 2) mesh_norm_vizu.edges.add(len(transformed_mesh_vertices)) vert_i = edge_i = 0 @@ -670,7 +678,7 @@ def visualise_normals(name, transformed_mesh_vertices, mesh_normals, import_scal # mesh_norm_vizu.validate() mesh_norm_vizu.update() # object_norm_vizu.select = True - # bpy.context.scene.objects.active = object_norm_vizu + # bpy.context.collection.objects.active = object_norm_vizu else: print("WARNING! 'visualise_normals' - NO MESH NORMALS PROVIDED!") ''' @@ -760,6 +768,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, mesh_uv, mesh_tuv, @@ -772,6 +781,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa mesh_normals, mesh_rgb, mesh_rgba, + mesh_factor, scs_globals.import_welding_precision) objects_data[ob_index] = ( @@ -783,6 +793,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, mesh_uv, mesh_tuv, @@ -794,12 +805,14 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa # print('ob_material: %s' % ob_material) # print('mesh_vertices: %s' % mesh_vertices) # print('mesh_rgba 1: %s' % str(mesh_rgba)) + # print('mesh_factor: %s' % str(mesh_factor)) # print('mesh_uv count: %s' % len(mesh_uv)) # print('mesh_triangles: %s' % mesh_triangles) # print('mesh_faces: %s' % mesh_faces) # print('mesh_face_materials: %s' % mesh_face_materials) # print('mesh_edges: %s' % mesh_edges) # print('piece_count: %s' % str(piece_count)) + # print('---------------------') piece_count -= 1 elif section.type == 'Part': if scs_globals.import_pim_file: @@ -941,13 +954,14 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa objects_data[obj_i][5], # mesh_tangents objects_data[obj_i][6], # mesh_rgb objects_data[obj_i][7], # mesh_rgba - objects_data[obj_i][8], # mesh_scalars + objects_data[obj_i][8], # mesh_factor + objects_data[obj_i][9], # mesh_scalars object_skinning, - objects_data[obj_i][9], # mesh_uv - objects_data[obj_i][10], # mesh_tuv - objects_data[obj_i][11], # mesh_triangles + objects_data[obj_i][10], # mesh_uv + objects_data[obj_i][11], # mesh_tuv + objects_data[obj_i][12], # mesh_triangles materials_data, - objects_data[obj_i][12], # points_to_weld_list + objects_data[obj_i][13], # points_to_weld_list terrain_points_trans, ) diff --git a/addon/io_scs_tools/imp/pim_ef.py b/addon/io_scs_tools/imp/pim_ef.py index 758289f6..d8c8900b 100644 --- a/addon/io_scs_tools/imp/pim_ef.py +++ b/addon/io_scs_tools/imp/pim_ef.py @@ -80,6 +80,7 @@ def _get_piece_streams(section): mesh_uv_aliases = {} mesh_rgb = {} mesh_rgba = {} + mesh_factor = {} mesh_scalars = {} for sec in section.sections: if sec.type == "Stream": @@ -178,6 +179,12 @@ def _get_piece_streams(section): # print(' face_data_block:\n%s' % str(face_data_block)) mesh_rgb[face_stream_name].append(face_data_block) + elif face_stream_tag.startswith("_FACTOR") and face_stream_type == "FLOAT4": + if face_stream_name not in mesh_factor: + mesh_factor[face_stream_name] = [] + # print(' face_data_block:\n%s' % str(face_data_block)) + mesh_factor[face_stream_name].append(face_data_block) + elif face_stream_tag.startswith("_UV") and face_stream_type == "FLOAT2": if face_stream_name not in mesh_uv: mesh_uv[face_stream_name] = [] @@ -204,6 +211,7 @@ def _get_piece_streams(section): mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, mesh_uv, mesh_uv_aliases, @@ -223,6 +231,7 @@ def _create_piece( mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, object_skinning, mesh_uv, @@ -287,6 +296,11 @@ def _create_piece( if mesh_rgb: mesh_rgb_final.update(mesh_rgb) + # VERTEX FACTORS + mesh_factor_final = [] + if mesh_factor: + mesh_factor_final = mesh_factor + vcolor_corrupt = False for vc_layer_name in mesh_rgb_final: @@ -304,6 +318,9 @@ def _create_piece( if vcolor_corrupt: lprint("W Piece %r has vertices with vertex color greater the 1.0, clamping it!", (name,)) + for vfc_layer_name in mesh_factor_final: + _mesh_utils.bm_make_vfc_layer(7, bm, vfc_layer_name, mesh_factor_final[vfc_layer_name]) + bm.to_mesh(mesh) mesh.update() bm.free() @@ -316,15 +333,14 @@ def _create_piece( # NORMALS - has to be applied after bmesh creation as they are set directly to mesh if _get_scs_globals().import_use_normals: - mesh.create_normals_split() - # first set normals directly to loops + clnors = [] for poly_i, poly in enumerate(mesh.polygons): for poly_loop_i, loop_i in enumerate(poly.loop_indices): curr_n = _convert_utils.scs_to_blend_matrix() @ Vector(mesh_normals[poly_i][poly_loop_i]) - mesh.loops[loop_i].normal[:] = curr_n + clnors.extend(curr_n) # then we have to go trough very important step they say, # as without validation we get wrong result for some normals @@ -334,13 +350,8 @@ def _create_piece( mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) # finally fill clnors from loops normals and apply them (taken from official Blenders scripts) - clnors = array.array('f', [0.0] * (len(mesh.loops) * 3)) - mesh.loops.foreach_get("normal", clnors) mesh.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) - mesh.use_auto_smooth = True - mesh.auto_smooth_angle = 3.14 - mesh.free_normals_split() else: # set polygons to use smooth representation only mesh.polygons.foreach_set("use_smooth", [True] * len(mesh.polygons)) @@ -538,6 +549,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, mesh_uv, mesh_uv_aliases, @@ -554,6 +566,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa mesh_tangents, mesh_rgb, mesh_rgba, + mesh_factor, mesh_scalars, mesh_uv, mesh_uv_aliases, @@ -567,6 +580,7 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa # print('ob_material: %s' % ob_material) # print('mesh_vertices: %s' % mesh_vertices) # print('mesh_rgba 1: %s' % str(mesh_rgba)) + # print('mesh_factor: %s' % str(mesh_factor)) # print('mesh_uv count: %s' % len(mesh_uv)) # print('mesh_triangles: %s' % mesh_triangles) # print('mesh_faces: %s' % mesh_faces) @@ -689,14 +703,15 @@ def load_pim_file(context, filepath, terrain_points_trans=None, preview_model=Fa objects_data[obj_i][4], # mesh_tangents objects_data[obj_i][5], # mesh_rgb objects_data[obj_i][6], # mesh_rgba - objects_data[obj_i][7], # mesh_scalars + objects_data[obj_i][7], # mesh_factor + objects_data[obj_i][8], # mesh_scalars object_skinning, - objects_data[obj_i][8], # mesh_uv - objects_data[obj_i][9], # mesh_uv_aliases - objects_data[obj_i][10], # mesh_tuv - objects_data[obj_i][11], # mesh_faces - objects_data[obj_i][12], # mesh_face_materials - objects_data[obj_i][13], # mesh_edges + objects_data[obj_i][9], # mesh_uv + objects_data[obj_i][10], # mesh_uv_aliases + objects_data[obj_i][11], # mesh_tuv + objects_data[obj_i][12], # mesh_faces + objects_data[obj_i][13], # mesh_face_materials + objects_data[obj_i][14], # mesh_edges terrain_points_trans, materials_data, ) diff --git a/addon/io_scs_tools/imp/pip.py b/addon/io_scs_tools/imp/pip.py index 6da37c3b..92c17f88 100644 --- a/addon/io_scs_tools/imp/pip.py +++ b/addon/io_scs_tools/imp/pip.py @@ -207,7 +207,7 @@ def _get_sign_properties(section): def _get_spawn_properties(section): """Receives a Spawn section and returns its properties in its own variables. For any item that fails to be found, it returns None.""" - spawn_name = spawn_position = spawn_rotation = spawn_type = None + spawn_name = spawn_position = spawn_rotation = spawn_type = spawn_flags = None for prop in section.props: if prop[0] in ("", "#"): pass @@ -219,9 +219,11 @@ def _get_spawn_properties(section): spawn_rotation = prop[1] elif prop[0] == "Type": spawn_type = prop[1] + elif prop[0] == "Flags": + spawn_flags = prop[1] else: lprint('\nW Unknown property in "Spawn" data: "%s"!', prop[0]) - return spawn_name, spawn_position, spawn_rotation, spawn_type + return spawn_name, spawn_position, spawn_rotation, spawn_type, spawn_flags def _get_t_light_properties(section): @@ -450,12 +452,23 @@ def _create_spawn_locator( spawn_name, spawn_position, spawn_rotation, - spawn_type + spawn_type, + spawn_flags ): locator = _object_utils.create_locator_empty(spawn_name, spawn_position, spawn_rotation, (1, 1, 1), 0.1, 'Prefab') if locator: locator.scs_props.locator_prefab_type = 'Spawn Point' locator.scs_props.locator_prefab_spawn_type = str(spawn_type) + + # flags for custom spawn type + + # check if Spawn Type = Custom (9) + if locator.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.CUSTOM): + locator.scs_props.locator_prefab_custom_depot_type = str(spawn_flags & _PL_consts.PSPCF.DEPOT_TYPE_MASK) + locator.scs_props.locator_prefab_custom_parking_difficulty = str(spawn_flags & _PL_consts.PSPCF.DIFFICULTY_MASK) + locator.scs_props.locator_prefab_custom_lenght = str(spawn_flags & _PL_consts.PSPCF.LENGHT_MASK) + locator.scs_props.locator_prefab_custom_rule = str(spawn_flags & _PL_consts.PSPCF.TRAILER_MASK) + return locator @@ -732,18 +745,23 @@ def load(filepath, terrain_points_trans): (spawn_name, spawn_position, spawn_rotation, - spawn_type) = _get_spawn_properties(section) + spawn_type, + spawn_flags) = _get_spawn_properties(section) if spawn_name is None: spawn_name = str('Sign_Locator_' + str(spawn_index)) else: spawn_name = _name_utils.get_unique(spawn_name, spawn_points_data.keys()) + if spawn_flags is None: + spawn_flags = int(0) + spawn_points_data[spawn_name] = ( spawn_index, spawn_position, spawn_rotation, spawn_type, + spawn_flags, ) spawn_index += 1 elif section.type == 'Semaphore': # former "TrafficLight" @@ -915,6 +933,7 @@ def load(filepath, terrain_points_trans): spawn_points_data[name][1], spawn_points_data[name][2], spawn_points_data[name][3], + spawn_points_data[name][4], # sp_flags ) if loc: _print_locator_result(loc, "Spawn Point", name) diff --git a/addon/io_scs_tools/imp/pit.py b/addon/io_scs_tools/imp/pit.py index b72cacf0..552b46f4 100644 --- a/addon/io_scs_tools/imp/pit.py +++ b/addon/io_scs_tools/imp/pit.py @@ -153,7 +153,10 @@ def _get_look(section): # now strip flipflake flavor string, remove flipflake related texture & report it if has_flipflake: - mat_effect = mat_effect.replace(".flipflake", "") + if mat_effect.find(".flipflakeuv") != -1: + mat_effect = mat_effect.replace(".flipflakeuv", "") + else: + mat_effect = mat_effect.replace(".flipflake", "") if textures.pop("texture_flakenoise", None): sec.remove_section("Texture", "Tag", r"^[\w\[\]]+:texture_flakenoise$") @@ -181,6 +184,14 @@ def _get_look(section): lprint("W Needless truckpaint texture: 'texture_paintjob' in current material configuration inside material %r, ignoring it!", (mat_alias,)) + # Extra treatment for outdated env_factor in eut2.glass & eut2.truckpaint shaders + # This attribute is outdated and is not used anymore, but it is still present in many materials. + if mat_effect.startswith("eut2.glass") or mat_effect.startswith("eut2.truckpaint"): + # remove env_factor attribute + if attributes.pop("env_factor", None) is not None: + lprint("W Needless attribute: 'env_factor' in current material configuration inside material %r, ignoring it!", + (mat_alias,)) + # Extra treatment for building shaders # # If night version of it is detected, switch it to day one. @@ -189,6 +200,47 @@ def _get_look(section): mat_effect = mat_effect.replace(".night", ".day") lprint("W Night version of building shader detected in material %r, switching it to day!", (mat_alias,)) + # Extra treatment for deprecated/removed/unsupported shaders and flavors + # + # If day/night version of "window" shader is detected, switch it to "lit". + if mat_effect.startswith("eut2.window") and mat_effect.endswith((".day", ".night")): + + mat_effect = mat_effect.replace(".day", ".lit").replace(".night", ".lit") + lprint("W Outdated Day/Night version of window shader detected in material %r, switching it to lit!", (mat_alias,)) + + # If tsnmap16/tsnmapuv16 flavor is detected, switch it to "tsnmap/tsnmapuv + if any(x in mat_effect for x in (".tsnmap16", ".tsnmapuv16")): + + mat_effect = mat_effect.replace(".tsnmap16", ".tsnmap").replace(".tsnmapuv16", ".tsnmapuv") + lprint('W Outdated tsnmap16/tsnmapuv16 flavor detected in material %r, switching it to tsnmap/tsnmapuv!', (mat_alias,)) + + # If day/night version of "eut2.billboard" shader is detected, remove "day/night" and in "night" case, switch to "eut2.billboard.lit" + if mat_effect.startswith("eut2.billboard") and mat_effect.endswith((".day", ".night")): + + if mat_effect.endswith(".night"): + mat_effect = mat_effect.replace("billboard", "billboard.lit") + lprint("W Night version of billboard shader detected in material %r, switching it to lit!", (mat_alias,)) + else: + mat_effect = mat_effect.replace(".day", "") + lprint("W Day version of billboard shader detected in material %r, removing it from effect!", (mat_alias,)) + + # Extra temporary treatment for new attributes not supported in older material format used by BT + # + # If "diffuse_secondary" attribute is detected, remove it + if attributes.pop("diffuse_secondary", None) is not None: + lprint("I Unsupported attribute: 'diffuse_secondary' in current material configuration inside material %r, ignoring it!", + (mat_alias,)) + + # If "is_dynamic_road" attribute is detected, remove it + if attributes.pop("is_dynamic_road", None) is not None: + lprint("W Unsupported attribute: 'is_dynamic_road' inside material %r, ignoring it! Material will not work as intended after export!", + (mat_alias,)) + + # If "is_terrain_material" attribute is detected, remove it + if attributes.pop("is_terrain_material", None) is not None: + lprint("W Unsupported attribute: 'is_terrain_material' inside material %r, ignoring it! Material will not work as intended after export!", + (mat_alias,)) + look_mat_settings[mat_alias] = (mat_effect, mat_flags, attributes, textures, sec) return look_name, look_mat_settings diff --git a/addon/io_scs_tools/imp/pix.py b/addon/io_scs_tools/imp/pix.py index f9b2b333..a3fc1b10 100644 --- a/addon/io_scs_tools/imp/pix.py +++ b/addon/io_scs_tools/imp/pix.py @@ -174,6 +174,7 @@ def _create_scs_root_object(name, loaded_variants, loaded_looks, mats_info, obje bpy.context.view_layer.active_layer_collection.collection.objects.link(scs_root_object) bpy.context.view_layer.objects.active = scs_root_object scs_root_object.scs_props.scs_root_object_export_enabled = True + scs_root_object["skip_default_part"] = True scs_root_object.scs_props.empty_object_type = 'SCS_Root' # print('LOD.pos: %s' % str(scs_root_object.location)) @@ -233,7 +234,9 @@ def _create_scs_root_object(name, loaded_variants, loaded_looks, mats_info, obje part.include = True # cleanup generated terrain points vertex layers by variant - for obj in parts_dict[part.name]: + # Because "collect_parts_on_root" return ONLY dictionary of USED parts, parts_dict must include empty list for cases, + # where part.name is not existing in parts_dict (especially for "defaultpart" which is always included, and can be used also as empty part)) + for obj in parts_dict.get(part.name, []): if obj.type != "MESH": continue diff --git a/addon/io_scs_tools/internals/callbacks/open_gl.py b/addon/io_scs_tools/internals/callbacks/open_gl.py index 07958cf1..bdf65512 100644 --- a/addon/io_scs_tools/internals/callbacks/open_gl.py +++ b/addon/io_scs_tools/internals/callbacks/open_gl.py @@ -19,7 +19,6 @@ # Copyright (C) 2013-2019: SCS Software import bpy -import console_python from io_scs_tools.internals.open_gl import core as _gl_core from io_scs_tools.utils import view3d as _view3d_utils @@ -47,7 +46,7 @@ def enable(mode="Normal"): _callback_handle[:] = handle_post_pixel, handle_post_view - console_python.execute.hooks.append((_view3d_utils.tag_redraw_all_view3d, ())) + _view3d_utils.tag_redraw_all_view3d() def disable(): @@ -58,7 +57,7 @@ def disable(): if not _callback_handle: return - console_python.execute.hooks.remove((_view3d_utils.tag_redraw_all_view3d, ())) + _view3d_utils.tag_redraw_all_view3d() handle_post_pixel, handle_post_view = _callback_handle diff --git a/addon/io_scs_tools/internals/callbacks/persistent.py b/addon/io_scs_tools/internals/callbacks/persistent.py index 30ea3002..9f9ec5e0 100644 --- a/addon/io_scs_tools/internals/callbacks/persistent.py +++ b/addon/io_scs_tools/internals/callbacks/persistent.py @@ -25,6 +25,7 @@ from io_scs_tools.internals.persistent import file_load as _persistent_file_load from io_scs_tools.internals.persistent import open_gl as _persistent_open_gl from io_scs_tools.internals.persistent import shaders_update as _persistent_shaders_update +from io_scs_tools.internals.persistent import active_part as _persistent_active_part def enable(): @@ -51,6 +52,7 @@ def enable(): bpy.app.handlers.frame_change_post.append(_persistent_shaders_update.post_frame_change) bpy.app.handlers.undo_post.append(_persistent_open_gl.post_undo) bpy.app.handlers.redo_post.append(_persistent_open_gl.post_redo) + bpy.app.handlers.depsgraph_update_post.append(_persistent_active_part.sync_part) bpy.app.handlers.load_post.append(_persistent_file_load.post_load) @@ -79,3 +81,5 @@ def disable(): bpy.app.handlers.load_post.remove(_persistent_file_load.post_load) if _persistent_file_save.pre_save in bpy.app.handlers.save_pre: bpy.app.handlers.save_pre.remove(_persistent_file_save.pre_save) + if _persistent_active_part.sync_part in bpy.app.handlers.depsgraph_update_post: + bpy.app.handlers.depsgraph_update_post.remove(_persistent_active_part.sync_part) diff --git a/addon/io_scs_tools/internals/containers/config.py b/addon/io_scs_tools/internals/containers/config.py index e2228a30..def0febc 100644 --- a/addon/io_scs_tools/internals/containers/config.py +++ b/addon/io_scs_tools/internals/containers/config.py @@ -261,7 +261,10 @@ def apply_settings(self, settings_to_apply=None): if not attr: continue - setattr(scs_globals, attr, value) + # prevents the configuration from looping when starting Blender + current = getattr(scs_globals, attr, None) + if current != value: + setattr(scs_globals, attr, value) def fill_from_pix_section(self, section): """Fill config section with data from given pix section. @@ -313,7 +316,7 @@ def __init__(self): "Source": (str, get_combined_ver_str(), None), "Type": (str, "Configuration", None), "Note": (str, "User settings of SCS Blender Tools", None), - # "Author": (str, bpy.context.user_preferences.system.author, None), + # "Author": (str, bpy.context.preferences.system.author, None), "ConfigStoragePlace": (str, get_default(scs_globals, 'config_storage_place'), 'config_storage_place'), "DumpLevel": (str, get_default(scs_globals, 'dump_level'), 'dump_level') } @@ -501,6 +504,7 @@ def __init__(self): "CurveSegments": (int, get_default(scs_globals, 'curve_segments'), 'curve_segments'), "DisplayTextInfo": (str, get_default(scs_globals, 'display_info'), 'display_info'), "IconTheme": (str, _ICONS_consts.default_icon_theme, 'icon_theme'), + "ShowTrailerType": (int, get_default(scs_globals, 'show_trailer_type'), 'show_trailer_type'), } @@ -523,6 +527,28 @@ def __init__(self): "TriggerLineBase": (tuple, get_default(scs_globals, 'tp_connection_base_color'), 'tp_connection_base_color'), "InfoText": (tuple, get_default(scs_globals, 'info_text_color'), 'info_text_color'), "BasePaint": (tuple, get_default(scs_globals, 'base_paint_color'), 'base_paint_color'), + "TrailerLoadEasy": (tuple, get_default(scs_globals, 'trailer_load_easy_color'), 'trailer_load_easy_color'), + "TrailerLoadMedium": (tuple, get_default(scs_globals, 'trailer_load_medium_color'), 'trailer_load_medium_color'), + "TrailerLoadHard": (tuple, get_default(scs_globals, 'trailer_load_hard_color'), 'trailer_load_hard_color'), + "TrailerUnloadEasy": (tuple, get_default(scs_globals, 'trailer_unload_easy_color'), 'trailer_unload_easy_color'), + "TrailerUnloadMedium": (tuple, get_default(scs_globals, 'trailer_unload_medium_color'), 'trailer_unload_medium_color'), + "TrailerUnloadHard": (tuple, get_default(scs_globals, 'trailer_unload_hard_color'), 'trailer_unload_hard_color'), + "OwnedTrailer": (tuple, get_default(scs_globals, 'owned_trailer_color'), 'owned_trailer_color'), + "ServiceStation": (tuple, get_default(scs_globals, 'service_station_color'), 'service_station_color'), + } + + +class GlobalOther(_ConfigSection): + """Class for global other settings.""" + + def __init__(self): + """Constructor.""" + super().__init__("GlobalOther") + + scs_globals = _get_scs_globals() + self.props = { + "ActivateNewParts": (int, get_default(scs_globals, 'activate_new_parts'), 'activate_new_parts'), + "ActivateNewVariantParts": (int, get_default(scs_globals, 'activate_new_variant_parts'), 'activate_new_variant_parts'), } @@ -538,6 +564,7 @@ def __init__(self): "Export": Export(), "GlobalDisplay": GlobalDisplay(), "GlobalColors": GlobalColors(), + "GlobalOther": GlobalOther(), } def set_property(self, section_type, prop_name, value): diff --git a/addon/io_scs_tools/internals/containers/mat.py b/addon/io_scs_tools/internals/containers/mat.py index e1d14448..78b5cea7 100644 --- a/addon/io_scs_tools/internals/containers/mat.py +++ b/addon/io_scs_tools/internals/containers/mat.py @@ -25,8 +25,8 @@ class MatContainer: - def __init__(self, data_dict, effect): - """Create MAT file container with mapped data dictionary on attributes and textures. + def __init__(self, data_dict, effect, mat_format): + """Create MAT file container with mapped data dictionary on attributes, textures and tobjs data. It also stores material effect name. :param data_dict: all attributes from material represented with dictionary, @@ -34,33 +34,81 @@ def __init__(self, data_dict, effect): :type data_dict: dict[str, object] :param effect: shader effect full name :type effect: str + :param mat_format: material format (material|effect) + :type mat_format: str """ self.__effect = "" + self.__mat_format = "" self.__attributes = {} self.__textures = {} + self.__tobjs = {} if effect is not None: self.__effect = effect + if format is not None: + self.__mat_format = mat_format + for key in data_dict.keys(): - if key.startswith("texture"): + if mat_format == "material": - tex_type = "texture_name" - tex_val = "texture" + if key.startswith("texture"): - # take care of texture saved as arrays eg: texture[0] - if key.find("[") != -1: + tex_type = "texture_name" + tex_val = "texture" - tex_type = "texture_name" + key[key.find("["):] - tex_val = "texture" + key[key.find("["):] + # take care of texture saved as arrays eg: texture[0] + if key.find("[") != -1: - self.__textures[data_dict[tex_type]] = data_dict[tex_val] + tex_type = "texture_name" + key[key.find("["):] + tex_val = "texture" + key[key.find("["):] - else: + self.__textures[data_dict[tex_type]] = data_dict[tex_val] + + else: + + self.__attributes[key.replace("[", "").replace("]", "")] = data_dict[key] + + elif mat_format == "effect": + + # parse textures & tobjs + if key == "texture": + + for tex_type in data_dict[key].keys(): + tex_val = data_dict[key][tex_type] + self.__textures[tex_type] = tex_val["source"] + + # w_address for 3d cube reflections? not used in blender + attr_keys = ["u_address", "v_address"] + + # initialize empty tobj data + tobjs = [None] * len(attr_keys) + + # technically, i can return true/false if "repeat", but for eventual future use, + # it's better to return the actual value for now. + for i, attr in enumerate(attr_keys): + if "sampler" in tex_val: + tobjs[i] = "repeat" + + elif attr in tex_val: + if tex_val[attr].startswith("repeat"): + tobjs[i] = "repeat" + elif tex_val[attr].startswith("clamp"): + tobjs[i] = "extend" + elif tex_val[attr].startswith("mirror"): + tobjs[i] = "mirror" + + self.__tobjs[tex_type] = tuple(tobjs) + + # parse attributes + else: - self.__attributes[key.replace("[", "").replace("]", "")] = data_dict[key] + self.__attributes[key.replace("[", "").replace("]", "")] = data_dict[key] + + else: + lprint("E Unsupported MAT format %r!", (mat_format,)) def get_textures(self): """Returns shader textures defined in MAT container. @@ -69,6 +117,13 @@ def get_textures(self): """ return self.__textures + def get_tobjs(self): + """Returns textures tobj data defined in MAT container. + + :rtype: dict[str, tuple] + """ + return self.__tobjs + def get_attributes(self): """Returns shader attributes defined in MAT container. @@ -83,6 +138,13 @@ def get_effect(self): """ return self.__effect + def get_format(self): + """Returns material format defined in MAT container. + + :rtype: str + """ + return self.__mat_format + def get_data_from_file(filepath): """Returns entire data in data container from specified raw material file. @@ -94,14 +156,14 @@ def get_data_from_file(filepath): if filepath: if os.path.isfile(filepath) and filepath.lower().endswith(".mat"): - data_dict, effect = _mat.read_data(filepath) + data_dict, effect, mat_format = _mat.read_data(filepath) if data_dict: if len(data_dict) < 1: lprint('\nI MAT file "%s" is empty!', (_path_utils.readable_norm(filepath),)) return None - container = MatContainer(data_dict, effect) + container = MatContainer(data_dict, effect, mat_format) else: lprint('\nI MAT file "%s" is empty!', (_path_utils.readable_norm(filepath),)) return None diff --git a/addon/io_scs_tools/internals/containers/parsers/mat.py b/addon/io_scs_tools/internals/containers/parsers/mat.py index f62fb4ff..2cd5ebe6 100644 --- a/addon/io_scs_tools/internals/containers/parsers/mat.py +++ b/addon/io_scs_tools/internals/containers/parsers/mat.py @@ -3,6 +3,38 @@ from io_scs_tools.utils.printout import lprint +def parse_attributes(content, attr_pattern, print_info=False): + """Parse attributes from content and return them as dictionary. + + :param content: content of material file + :type content: str + :param attr_pattern: regex pattern for matching one attribute + :type attr_pattern: re.Pattern + :param print_info: switch for printing parsing info + :type print_info: bool + :return: dictionary of mapped material attributes + :rtype: dict + """ + attr_dict = {} + for i, attr_m in enumerate(attr_pattern.finditer(content)): + + attr_name = attr_m.group("attr_name") + attr_value = attr_m.group("attr_val").replace("{", "(").replace("}", ")") + + try: + parsed_attr_value = literal_eval(attr_value) + except ValueError: + parsed_attr_value = None + lprint("W Ignoring unrecognized/malformed MAT file attribute:\n\t Name: %r; Value: %r;", (attr_name, attr_value)) + + if print_info: + print("\tName:", attr_name, " -> Value(", type(parsed_attr_value).__name__, "):", parsed_attr_value) + + # fill successfully parsed values into dictionary + if parsed_attr_value is not None: + attr_dict[attr_name] = parsed_attr_value + + return attr_dict def read_data(filepath, print_info=False): """Reads data from mat file and returns it's attributes as dictionary. @@ -11,24 +43,27 @@ def read_data(filepath, print_info=False): :type filepath: str :param print_info: switch for printing parsing info :type print_info: bool - :return: tuple of dictionary of mapped material attributes and effect name - :rtype: (dict, effect) + :return: tuple of dictionary of mapped material attributes, effect name and mat format + :rtype: (dict, effect, mat_format) """ + _FORMAT_G = "mat_format" _EFFECT_G = "effect" _CONTENT_G = "content" _ATTR_NAME_G = "attr_name" _ATTR_VALUE_G = "attr_val" + _ATTR_VALUE_NEST_G = "attr_val_nest" if print_info: print('** MAT Parser ...') print(' filepath: %r' % str(filepath)) - material_pattern = compile(r'material\W*:\W*\"(?P<%s>(.|\n)+)\"\W*\{\W*(?P<%s>(.|\n)+)\W*\}' % (_EFFECT_G, _CONTENT_G)) + # skip any whitespace at the beginning of file (game does not care about it), then match file format (not hardcoded to later check if it's supported), ), effect name and content + material_pattern = compile(r'(^\s*)(?P<%s>[\S]+)\W*:\W*\"(?P<%s>[\w.]+)\"\W*\{\W*(?P<%s>(.|\n)+)\W*\}' % (_FORMAT_G, _EFFECT_G, _CONTENT_G)) """Regex pattern for matching whole material file.""" attr_pattern = compile(r'(?P<%s>.+):(?P<%s>.+)' % (_ATTR_NAME_G, _ATTR_VALUE_G)) """Regex pattern for matching one attribute.""" - - attr_dict = {} + nested_pattern = compile(r'(?P<%s>\w+):"(?P<%s>[^"]+)"\{(?P<%s>[^}]+)\}' % (_ATTR_NAME_G ,_ATTR_VALUE_G, _ATTR_VALUE_NEST_G)) + """Regex pattern for matching nested data in content.""" with open(filepath, encoding="utf8") as f: f_data = f.read() @@ -37,29 +72,65 @@ def read_data(filepath, print_info=False): # match whole file m = material_pattern.match(f_data) - effect = m.group(_EFFECT_G) + mat_format = m.group(_FORMAT_G) + effect = m.group(_EFFECT_G).replace(".rfx", "") content = m.group(_CONTENT_G).replace(" ", "").replace("\t", "") if print_info: + print("Format:", mat_format) print("Effect:", effect) - print("Content:", content) + print("Content:\n", content, sep='') - for i, attr_m in enumerate(attr_pattern.finditer(content)): + if mat_format == "material": + attr_dict = parse_attributes(content, attr_pattern, print_info) - attr_name = attr_m.group(_ATTR_NAME_G) - attr_value = attr_m.group(_ATTR_VALUE_G).replace("{", "(").replace("}", ")") + elif mat_format == "effect": + + nested_list = {} + + # extract nested data from content + for i, attr_m in enumerate(nested_pattern.finditer(content)): + attr_name = attr_m.group(_ATTR_NAME_G) + attr_value = attr_m.group(_ATTR_VALUE_G) + attr_value_nest = attr_m.group(_ATTR_VALUE_NEST_G) + + if attr_name not in nested_list: + nested_list[attr_name] = {} + + # parse nested_attr_value into a dictionary + nested_attr_value_dict = {} + for i, n_attr_m in enumerate(attr_pattern.finditer(attr_value_nest)): + + nested_attr_name = n_attr_m.group(_ATTR_NAME_G) + nested_attr_value = n_attr_m.group(_ATTR_VALUE_G).replace("{", "(").replace("}", ")").replace('"', '') + + if print_info: + print("\tName:", nested_attr_name, " -> Value(", type(nested_attr_value).__name__, "):", nested_attr_value) + + nested_attr_value_dict[nested_attr_name] = nested_attr_value + + nested_list[attr_name][attr_value] = nested_attr_value_dict + + # remove nested data from content to not broke parsing of normal attributes + content = nested_pattern.sub('', content) + + if print_info: + print("Nested dict:\n", nested_list, sep='') + + # parse attributes + attr_dict = parse_attributes(content, attr_pattern, print_info) + + if print_info: + print("Attr dict:\n", attr_dict, sep='') - try: - parsed_attr_value = literal_eval(attr_value) - except ValueError: - parsed_attr_value = None - lprint("W Ignoring unrecognized/malformed MAT file attribute:\n\t Name: %r; Value: %r;", (attr_name, attr_value)) + # combine nested_list with attr_dict + for name, value in nested_list.items(): + attr_dict[name] = value if print_info: - print("\tName:", attr_name, " -> Value(", type(parsed_attr_value).__name__, "):", parsed_attr_value) + print("Combined dict:\n", attr_dict, sep='') - # fill successfully parsed values into dictionary - if parsed_attr_value is not None: - attr_dict[attr_name] = parsed_attr_value + else: + lprint("E Unknown mat format: `%s`", (mat_format, )) - return attr_dict, effect + return attr_dict, effect, mat_format diff --git a/addon/io_scs_tools/internals/containers/parsers/mat_convert.py b/addon/io_scs_tools/internals/containers/parsers/mat_convert.py new file mode 100644 index 00000000..dc225534 --- /dev/null +++ b/addon/io_scs_tools/internals/containers/parsers/mat_convert.py @@ -0,0 +1,143 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +# Converting script based on the mwl4 script from https://github.com/mwl4/ConverterPIX/ + +class AttributeConvert: + def __init__(self, name, value_count, start_index): + self.name = name + self.value_count = value_count + self.start_index = start_index + +default_map_to_material = { + "additional_ambient": AttributeConvert("add_ambient", 1, 0), + "glass_tint_color": AttributeConvert("tint", 3, 0), + "glass_tint_opacity": AttributeConvert("tint_opacity", 1, 0), + "shadowmap_bias": AttributeConvert("shadow_bias", 1, 0), + "paintjob_base_color": AttributeConvert("aux[8]", 3, 0), + "specular_secondary": AttributeConvert("aux[3]", 4, 0), + "shininess_secondary": AttributeConvert("aux[3]", 4, 3), + "reflection_secondary": AttributeConvert("reflection2", 1, 0), + "lod_selector": AttributeConvert("aux[1]", 1, 0), + "shadow_offset": AttributeConvert("aux[0]", 1, 0), + "amod_decal_blending_factors": AttributeConvert("aux[0]", 2, 0), + "texgen_0_gen": AttributeConvert("aux[0]", 4, 0), + "texgen_0_rot": AttributeConvert("aux[0]", 4, 2), + "texgen_1_gen": AttributeConvert("aux[1]", 4, 0), + "texgen_1_rot": AttributeConvert("aux[1]", 4, 2), + "far_color": AttributeConvert("aux[2]", 4, 0), + "far_specular_power": AttributeConvert("aux[2]", 4, 3), + "depth_bias": AttributeConvert("aux[0]", 1, 0), + "luminance_output": AttributeConvert("aux[5]", 2, 0), + "luminance_night": AttributeConvert("aux[5]", 2, 1), + "interior_atlas_dimensions": AttributeConvert("aux[1]", 2, 0), + "interior_glass_color": AttributeConvert("aux[2]", 4, 0), + "interior_unit_room_dimensions": AttributeConvert("aux[0]", 2, 0), + "water_distances": AttributeConvert("aux[0]", 3, 0), + "water_near_color": AttributeConvert("aux[1]", 3, 0), + "water_horizon_color": AttributeConvert("aux[2]", 3, 0), + "water_layer_0_yaw": AttributeConvert("aux[3]", 4, 0), + "water_layer_0_speed": AttributeConvert("aux[3]", 4, 1), + "water_layer_0_scale": AttributeConvert("aux[3]", 4, 2), + "water_layer_1_yaw": AttributeConvert("aux[4]", 4, 0), + "water_layer_1_speed": AttributeConvert("aux[4]", 4, 1), + "water_layer_1_scale": AttributeConvert("aux[4]", 4, 2), + "water_mirror": AttributeConvert("aux[5]", 1, 0), + "animsheet_cfg_fps": AttributeConvert("aux[0]", 3, 0), + "animsheet_cfg_frames_row": AttributeConvert("aux[0]", 3, 1), + "animsheet_cfg_frames_total": AttributeConvert("aux[0]", 3, 2), + "animsheet_frame_width": AttributeConvert("aux[1]", 2, 0), + "animsheet_frame_height": AttributeConvert("aux[1]", 2, 1), + "detail_fadeout_from": AttributeConvert("aux[5]", 4, 0), + "detail_fadeout_range": AttributeConvert("aux[5]", 4, 1), + "detail_blend_bias": AttributeConvert("aux[5]", 4, 2), + "detail_uv_scale": AttributeConvert("aux[5]", 4, 3), + "animation_speed": AttributeConvert("aux[0]", 1, 0), + "showroom_r_color": AttributeConvert("aux[0]", 1, 0), + "showroom_speed": AttributeConvert("aux[4]", 3, 0), + "flake_uvscale": AttributeConvert("aux[5]", 4, 0), + "flake_shininess": AttributeConvert("aux[5]", 4, 1), + "flake_clearcoat_rolloff": AttributeConvert("aux[5]", 4, 2), + "flake_vratio": AttributeConvert("aux[5]", 4, 3), + "flake_color": AttributeConvert("aux[6]", 4, 0), + "flake_density": AttributeConvert("aux[6]", 4, 3), + "flip_color": AttributeConvert("aux[7]", 4, 0), + "flip_strength": AttributeConvert("aux[7]", 4, 3), + "mix00_diffuse_secondary": AttributeConvert("aux[0]", 3, 0), + "mult_uvscale": AttributeConvert("aux[5]", 4, 0), + "mult_uvscale_secondary": AttributeConvert("aux[5]", 4, 2), + "sheet_frame_size_r": AttributeConvert("aux[0]", 4, 0), + "sheet_frame_size_g": AttributeConvert("aux[0]", 4, 2), + "sheet_frame_size_b": AttributeConvert("aux[1]", 4, 0), + "sheet_frame_size_a": AttributeConvert("aux[1]", 4, 2), + "paintjob_r_color": AttributeConvert("aux[5]", 3, 0), + "paintjob_g_color": AttributeConvert("aux[6]", 3, 0), + "paintjob_b_color": AttributeConvert("aux[7]", 3, 0), +} + +class AttributeConverter: + def __init__(self): + self.temp_values = {} + + def effect_to_material(self, attribute, value): + """Converts effect attribute to material attribute if present in the mapping list. + + :param attribute: attribute name to convert + :type attribute: str + :param value: value of attribute to convert + :type value: int, float, tuple + :return: tuple of converted attribute name and value + :rtype: (attr, val) + """ + if attribute in default_map_to_material: + conversion = default_map_to_material[attribute] + attr = conversion.name + + if not isinstance(value, tuple): + value = (value,) + + # check for values in temp_values + if attr in self.temp_values: + val = list(self.temp_values[attr]) + else: + val = [None] * conversion.value_count + + # insert values into correct positions + for i, v in enumerate(value): + val[conversion.start_index + i] = v + + # save updated value in temp_values if not fully filled + if None in val or 0.0 in val: + self.temp_values[attr] = tuple(val) + else: + self.temp_values.pop(attr, None) + + # set none to 0.0 + val = tuple(v if v is not None else 0.0 for v in val) + + # return single value if only one + if len(val) == 1: + val = val[0] + + else: + attr = attribute + val = value + + return attr, val diff --git a/addon/io_scs_tools/internals/icons/__init__.py b/addon/io_scs_tools/internals/icons/__init__.py index c260b4d6..771f59c3 100644 --- a/addon/io_scs_tools/internals/icons/__init__.py +++ b/addon/io_scs_tools/internals/icons/__init__.py @@ -19,6 +19,7 @@ # Copyright (C) 2013-2019: SCS Software import os +import bpy from bpy.utils import previews from io_scs_tools.consts import Icons as _ICON_consts from io_scs_tools.utils import path as _path @@ -90,6 +91,25 @@ def register(): set_theme(get_theme_name(0)) print("WARNING\t- Default icon theme doesn't exist, fallback to first available!") + # Forces icons to reload (Because of issues with Vulkan on faster systems) + bpy.app.timers.register(reload_icons, first_interval=0.5) + +def reload_icons(): + """Forces icons to reload after a short delay (workaround for Vulkan issues).""" + + current_theme = _cache[CURRENT_THEME] + pcoll = _cache[PCOLLS].get(current_theme) + if not pcoll: + return + + icon_themes_dir = os.path.join(_path.get_addon_installation_paths()[0], 'ui', 'icons') + for icon_type in _ICON_consts.Types.as_list(): + icon_path = os.path.join(icon_themes_dir, current_theme, icon_type) + if os.path.isfile(icon_path): + if icon_type in pcoll: + del pcoll[icon_type] + pcoll.load(icon_type, icon_path, 'IMAGE', force_reload=True) + return None def unregister(): """Clearing preview collections for custom icons. Should be called on addon unregister. diff --git a/addon/io_scs_tools/internals/open_gl/core.py b/addon/io_scs_tools/internals/open_gl/core.py index 5a46dc58..350095dd 100644 --- a/addon/io_scs_tools/internals/open_gl/core.py +++ b/addon/io_scs_tools/internals/open_gl/core.py @@ -20,7 +20,7 @@ import bpy import blf -import bgl +import gpu from mathutils import Vector from gpu_extras.presets import draw_texture_2d from io_scs_tools.consts import Operators as _OP_consts @@ -119,35 +119,49 @@ def _draw_3dview_report(window, area, region): return # calculate dynamic left and top margins + pos_y_shift = 190 if bpy.context.preferences.system.use_region_overlap: pos_x = 65 + header_height = 0 # try to find tools region and properly adopt position X + # TOOLS region for reg in area.regions: if reg.type == 'TOOLS': pos_x = reg.width + 10 break - pos_y = region.height - 105 + + # HEADER region + for reg in area.regions: + if reg.type == 'HEADER' and reg.height > 1: + # Check if header is on the top (!=) + header_height = reg.height if region.y != reg.y else 0 + break + else: + # Subtract size of reg.height if header is OFF + header_height = -38 + break + + pos_y = region.height - pos_y_shift - header_height else: pos_x = 10 - pos_y = region.height - 80 + pos_y = region.height - pos_y_shift + 38 for space in area.spaces: if space.type == 'VIEW_3D' and space.overlay.show_stats: - pos_y = pos_y - 105 + pos_y = pos_y - pos_y_shift - 10 break # draw BT banner - (bindcode, width, height) = _Show3DViewReport.get_scs_banner_img_data(window) + (texture, width, height) = _Show3DViewReport.get_scs_banner_img_data(window) - bgl.glEnable(bgl.GL_BLEND) - bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA) - draw_texture_2d(bindcode, (pos_x - 5, pos_y), width, height) - bgl.glDisable(bgl.GL_BLEND) + gpu.state.blend_set("ALPHA") + draw_texture_2d(texture, (pos_x - 5, pos_y), width, height, is_scene_linear_with_rec709_srgb_target=True) + gpu.state.blend_set("NONE") # draw control buttons, if controls are enabled if _Show3DViewReport.has_controls(window): - blf.size(0, 20, 72) + blf.size(0, 20) blf.color(0, .8, .8, .8, 1) # set x and y offsets to report operator, so that area calculations for buttons can be calculated properly @@ -187,7 +201,7 @@ def _draw_3dview_report(window, area, region): # draw scroll controls if _Show3DViewReport.is_scrolled() and _Show3DViewReport.is_shown(): - blf.size(0, 16, 72) + blf.size(0, 16) # draw scroll up button scroll_up_pos = ( @@ -243,7 +257,7 @@ def _draw_3dview_report(window, area, region): # draw version string pos_y -= 12 - blf.size(0, 11, 72) + blf.size(0, 11) blf.color(0, 0.909803921568627, .631372549019608, .0627450980392157, 1) blf.shadow(0, 0, 0, 0, 0, 1) blf.position(0, pos_x, pos_y, 0) @@ -253,7 +267,7 @@ def _draw_3dview_report(window, area, region): # draw actual operator title and message if shown if _Show3DViewReport.is_shown(): - blf.size(0, 12, 72) + blf.size(0, 12) blf.color(0, 1, 1, 1, 1) blf.shadow(0, 0, 0, 0, 0, 1) @@ -322,7 +336,7 @@ def _draw_3dview_immediate_report(region): ) # set size of the immidete text - blf.size(0, 18, 72) + blf.size(0, 18) blf.color(0, .952, .635, .062, 1) # draw on center of the region @@ -457,16 +471,15 @@ def draw_custom_3d_elements(mode): return if mode == "Normal": - bgl.glEnable(bgl.GL_DEPTH_TEST) - bgl.glEnable(bgl.GL_BLEND) - bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA) + gpu.state.depth_test_set("LESS") + gpu.state.blend_set("ALPHA") # draw buffers _primitive.draw_buffers(bpy.context.space_data) if mode == "Normal": - bgl.glDisable(bgl.GL_DEPTH_TEST) - bgl.glDisable(bgl.GL_BLEND) + gpu.state.depth_test_set("NONE") + gpu.state.blend_set("NONE") def draw_custom_2d_elements(): @@ -495,7 +508,7 @@ def draw_custom_2d_elements(): return font_id = 0 # default font - blf.size(font_id, 12, 72) + blf.size(font_id, 12) blf.color(font_id, scs_globals.info_text_color[0], scs_globals.info_text_color[1], scs_globals.info_text_color[2], 1.0) blf.word_wrap(font_id, 999) blf.enable(font_id, blf.WORD_WRAP) diff --git a/addon/io_scs_tools/internals/open_gl/locators/prefab.py b/addon/io_scs_tools/internals/open_gl/locators/prefab.py index 0b60ba79..0bcc6a65 100644 --- a/addon/io_scs_tools/internals/open_gl/locators/prefab.py +++ b/addon/io_scs_tools/internals/open_gl/locators/prefab.py @@ -99,6 +99,291 @@ def draw_shape_spawn_point(mat, scs_globals): _primitive.append_line_vertex((mat @ Vector((0.0, 0.1299, 0.525))), color) _primitive.append_line_vertex((mat @ Vector((0.0, -0.1299, 0.675))), color) +def draw_shape_spawn_point_custom(mat, scs_globals, obj): + """ + Draws shape for "Custom" type "Locator" of "Spawn Point" type. + :param mat: + :param scs_globals: + :param obj: + :return: + """ + + # Draw main Spawn Point shape + draw_shape_spawn_point(mat, scs_globals) + + # Get locator properties + depot_type = int(obj.scs_props.locator_prefab_custom_depot_type) >> 2 + cust_lenght = int(obj.scs_props.locator_prefab_custom_lenght) >> 4 + parking_diff = int(obj.scs_props.locator_prefab_custom_parking_difficulty) + cust_rule = int(obj.scs_props.locator_prefab_custom_rule) >> 16 + + # Matrix without "Locator Size" + mat_orig = obj.matrix_world + + # Local variables + width = 3.4 # Full width of depot shape + height = 0.05 # Height above ground to prevent z-fight + lenght = 14 + cust_lenght # Lenght of depo shape (we assume that cust_lenght (index) corresponds to additional lenght in meters eg. 14m + (idx) 0 = 14m) + lenght_t = 5.0/2 # Lenght of trailer type shape + + # Load + if depot_type == 1: + match parking_diff: + case 1: # Easy + color = scs_globals.trailer_load_easy_color + case 2: # Medium + color = scs_globals.trailer_load_medium_color + case 3: # Hard + color = scs_globals.trailer_load_hard_color + case _: + color = (0.0, 1.0, 1.0, 1.0) + # Unload + else: + match parking_diff: + case 1: # Easy + color = scs_globals.trailer_unload_easy_color + case 2: # Medium + color = scs_globals.trailer_unload_medium_color + case 3: # Hard + color = scs_globals.trailer_unload_hard_color + case _: + color = (1.0, 1.0, 0.0, 1.0) + + # Set diffrent shape for "Unlimited" lenght (max size of last fixed lenght) + if lenght == 29.0: + lenght = 20.0 + + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght + 3.0, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, lenght + 1.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, lenght + 8.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght + 10.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, lenght + 8.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, lenght + 1.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght + 3.0, height))), color) + + # Depot + _primitive.append_line_vertex((mat_orig @ Vector((width/2, 0.0, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, 0.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, lenght, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght + 2.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, lenght, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, 0.0, height))), color) + + # Set shape for "Trailer Type" + height = height + 0.20 # Height override to move shape a little bit above ground + if (cust_rule != 0) and scs_globals.show_trailer_type: + match cust_rule: + case 1: # Box Trailer + # Bottom rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + + # Top rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 2))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 2))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 2))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 2))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 2))), color) + + # Edges + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 2))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 2))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 2))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 2))), color) + + case 2: # Tank Trailer + # Rear hexagon + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 - lenght_t, height + 0.25))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 - lenght_t, height + 0.75))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 - lenght_t, height + 1.00))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 - lenght_t, height + 0.75))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 - lenght_t, height + 0.25))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.00, lenght/2 - lenght_t, height))), color) + + # Front hexagon + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 + lenght_t, height + 0.25))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 + lenght_t, height + 0.75))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 + lenght_t, height + 1.00))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 + lenght_t, height + 0.75))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 + lenght_t, height + 0.25))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.00, lenght/2 + lenght_t, height))), color) + + # Edges + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 + lenght_t, height))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 - lenght_t, height + 0.25))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 + lenght_t, height + 0.25))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 - lenght_t, height + 0.75))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.433, lenght/2 + lenght_t, height + 0.75))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 - lenght_t, height + 1.00))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.000, lenght/2 + lenght_t, height + 1.00))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 - lenght_t, height + 0.75))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 + lenght_t, height + 0.75))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 - lenght_t, height + 0.25))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.433, lenght/2 + lenght_t, height + 0.25))), color) + + case 3: # Dump & Bulk + # Top rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 1))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 1))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 1))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 1))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 1))), color) + + # Edges + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 1))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght/2, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 1))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 1))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght/2, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 1))), color) + + case 4: # Platform, Log & Container + # Bottom rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + + # Top rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 0.5))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 0.5))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 0.5))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 0.5))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 0.5))), color) + + # Edges + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 0.5))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 0.5))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 0.5))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 0.5))), color) + + case 5: # Livestock + # left-right + _primitive.append_line_vertex((mat_orig @ Vector((-1.0, lenght/2, height + 1.0))), color) + _primitive.append_line_vertex((mat_orig @ Vector((1.0, lenght/2, height + 1.0))), color) + + # front-back + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght/2 + 1.0, height + 1.0))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght/2 - 1.0, height + 1.0))), color) + + # up-down + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght/2, height + 2.0))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, lenght/2, height))), color) + + case 6: # Log Trailer + # Bottom rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + + # Top rectangle + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 0.5))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 0.5))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 0.5))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 0.5))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 0.5))), color) + + # Edges + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 - lenght_t, height + 2))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((0.5, lenght/2 + lenght_t, height + 2))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 - lenght_t, height + 2))), color) + + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-0.5, lenght/2 + lenght_t, height + 2))), color) + + +def draw_shape_spawn_point_trailer(mat, scs_globals, obj, color_idx): + """ + Draws fixed shape for old trailer rails of "Spawn Point" type. + :param mat: + :param scs_globals: + :param obj: + :param color_idx: + :type color_idx: int + :return: + """ + + # Draw main Spawn Point shape + draw_shape_spawn_point(mat, scs_globals) + + # Matrix without "Locator Size" + mat_orig = obj.matrix_world + + # Local variables + width = 3.4 # Full width of depot shape + height = 0.05 # Height above ground to prevent z-fight + lenght = 20 # Lenght of depo shape (excluding "Unlimited" shape) + pos_0 = 0.0 # Default "0" position of shape (required when we need to move whole shape) + + # Load colors from settings + match color_idx: + case 0: # Load Easy + color = scs_globals.trailer_load_easy_color + case 1: # Unload Easy + color = scs_globals.trailer_unload_easy_color + case 2: # Unload Medium + color = scs_globals.trailer_unload_medium_color + case 3: # Unload Hard + color = scs_globals.trailer_unload_hard_color + case 4: # Owned Trailer + color = scs_globals.owned_trailer_color + case 5: # Service Station + color = scs_globals.service_station_color + pos_0 = -29.0 # Move shape position 29m back, because service station uses truck point, not trailer rear as position 0. + case _: + color = (1.0, 1.0, 0.0, 1.0) + + # Shape for "Unlimited" lenght (default for old rail system) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, pos_0 + lenght + 3.0, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, pos_0 + lenght + 1.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, pos_0 + lenght + 8.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, pos_0 + lenght + 10.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, pos_0 + lenght + 8.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, pos_0 + lenght + 1.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, pos_0 + lenght + 3.0, height))), color) + + # Depot + _primitive.append_line_vertex((mat_orig @ Vector((width/2, pos_0, height))), color) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, pos_0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((-width/2, pos_0 + lenght, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((0.0, pos_0 + lenght + 2.0, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, pos_0 + lenght, height))), color, is_strip=True) + _primitive.append_line_vertex((mat_orig @ Vector((width/2, pos_0, height))), color) + def draw_shape_traffic_light(mat, scs_globals): """ @@ -211,7 +496,22 @@ def draw_prefab_locator(obj, scs_globals): _primitive.draw_shape_y_axis(mat, empty_size) _primitive.draw_shape_z_axis_neg(mat, empty_size) if not obj.scs_props.locator_preview_model_present or not scs_globals.show_preview_models: - draw_shape_spawn_point(mat, scs_globals) + if obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.CUSTOM): + draw_shape_spawn_point_custom(mat, scs_globals, obj) + elif obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.TRAILER_POS): # Load (Easy) OLD + draw_shape_spawn_point_trailer(mat, scs_globals, obj, 0) + elif obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.UNLOAD_EASY_POS): + draw_shape_spawn_point_trailer(mat, scs_globals, obj, 1) + elif obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.UNLOAD_MEDIUM_POS): + draw_shape_spawn_point_trailer(mat, scs_globals, obj, 2) + elif obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.UNLOAD_HARD_POS): + draw_shape_spawn_point_trailer(mat, scs_globals, obj, 3) + elif obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.TRAILER_SPAWN): + draw_shape_spawn_point_trailer(mat, scs_globals, obj, 4) + elif obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.SERVICE_POS): + draw_shape_spawn_point_trailer(mat, scs_globals, obj, 5) + else: + draw_shape_spawn_point(mat, scs_globals) elif obj.scs_props.locator_prefab_type == 'Traffic Semaphore': _primitive.draw_shape_x_axis(mat, empty_size) @@ -259,6 +559,16 @@ def get_prefab_locator_comprehensive_info(obj): spawn_type_i = int(obj.scs_props.locator_prefab_spawn_type) textlines.append("Type: %s" % obj.scs_props.enum_spawn_type_items[spawn_type_i][1]) + if obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.CUSTOM): + depot_type_i = int(obj.scs_props.locator_prefab_custom_depot_type) + difficulty_i = int(obj.scs_props.locator_prefab_custom_parking_difficulty) + lenght_i = int(obj.scs_props.locator_prefab_custom_lenght) + trailer_i = int(obj.scs_props.locator_prefab_custom_rule) + + textlines.append("Difficulty: %s (%s)" % (obj.scs_props.enum_custom_depot_type_items[depot_type_i][1], + obj.scs_props.enum_custom_parking_difficulty_items[difficulty_i][1])) + textlines.append("Lenght: %s" % obj.scs_props.enum_custom_lenght_items[lenght_i][1]) + textlines.append("Trailer: %s" % obj.scs_props.enum_custom_rule_items[trailer_i][1]) elif obj.scs_props.locator_prefab_type == 'Traffic Semaphore': diff --git a/addon/io_scs_tools/internals/open_gl/primitive.py b/addon/io_scs_tools/internals/open_gl/primitive.py index a18f4834..d9212c28 100644 --- a/addon/io_scs_tools/internals/open_gl/primitive.py +++ b/addon/io_scs_tools/internals/open_gl/primitive.py @@ -18,9 +18,9 @@ # Copyright (C) 2013-2019: SCS Software -import bgl import blf import gpu +import struct from array import array from gpu_extras.batch import batch_for_shader from mathutils import Vector @@ -61,23 +61,23 @@ def __init__(self, buffer_type, draw_size, shader_type, attr_names): # depending on type setup callbacks executed before and after dispatching if buffer_type == _Buffer.Types.LINES: - self.__bgl_callback = bgl.glLineWidth - self.__bgl_callback_param_before = self.__draw_size - self.__bgl_callback_param_after = 1.0 + self.__gpu_callback = gpu.state.line_width_set + self.__gpu_callback_param_before = self.__draw_size + self.__gpu_callback_param_after = 1.0 self.__draw_type = 'LINES' elif buffer_type == _Buffer.Types.POINTS: - self.__bgl_callback = bgl.glPointSize - self.__bgl_callback_param_before = self.__draw_size - self.__bgl_callback_param_after = 1.0 + self.__gpu_callback = gpu.state.point_size_set + self.__gpu_callback_param_before = self.__draw_size + self.__gpu_callback_param_after = 1.0 self.__draw_type = 'POINTS' elif buffer_type == _Buffer.Types.TRIS: - self.__bgl_callback = lambda *args: None - self.__bgl_callback_param_before = None - self.__bgl_callback_param_after = None + self.__gpu_callback = lambda *args: None + self.__gpu_callback_param_before = None + self.__gpu_callback_param_after = None self.__draw_type = 'TRIS' def append_attr(self, attr_name, value): @@ -114,26 +114,39 @@ def draw(self, uniforms, space_3d): if self.__type == _Buffer.Types.TRIS and space_3d.shading.type == 'WIREFRAME': return - self.__bgl_callback(self.__bgl_callback_param_before) + # gpu.state.point_size_set/line_width_set(size) + self.__gpu_callback(self.__gpu_callback_param_before) # bind shader self.__shader.bind() # fill the uniforms to binded shader + clip_planes_data = None + num_clip_planes = 0 + for uniform_name, uniform_type, uniform_data, uniform_length, uniform_count in uniforms: - uniform_loc = self.__shader.uniform_from_name(uniform_name) - if uniform_type == float: - self.__shader.uniform_vector_float(uniform_loc, uniform_data, uniform_length, uniform_count) - elif uniform_type == int: - self.__shader.uniform_vector_int(uniform_loc, uniform_data, uniform_length, uniform_count) - else: - raise TypeError("Invalid uniform type: %s" % uniform_type) + if uniform_name == "clip_planes": + clip_planes_data = uniform_data + elif uniform_name == "num_clip_planes": + num_clip_planes = uniform_data[0] + + if clip_planes_data is None: + raise ValueError("Missing clip_planes in uniforms") + + pad_planes = b'\x00' * (24 * 4) # 96 bytes + padded_clip_planes = bytearray(pad_planes) + padded_clip_planes[:len(clip_planes_data)] = clip_planes_data + + ubo_data = struct.pack("24f 4i", *struct.unpack("24f", padded_clip_planes), num_clip_planes, 0, 0, 0) + ubo = gpu.types.GPUUniformBuf(ubo_data) + + self.__shader.uniform_block("clip_data", ubo) # create batch and dispatch draw batch = batch_for_shader(self.__shader, self.__draw_type, self.__data) batch.draw(self.__shader) - self.__bgl_callback(self.__bgl_callback_param_after) + self.__gpu_callback(self.__gpu_callback_param_after) def has_entries(self): """Checks if there is any antries in this buffer. @@ -902,7 +915,7 @@ def draw_rect_2d(positions, color): :param color: RGBA of rectangle :type color: tuple(float, float, float, float) """ - shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') + shader = gpu.shader.from_builtin('UNIFORM_COLOR') batch = batch_for_shader( shader, 'TRI_STRIP', { diff --git a/addon/io_scs_tools/internals/open_gl/shaders/__init__.py b/addon/io_scs_tools/internals/open_gl/shaders/__init__.py index 081ce4a5..33292fc2 100644 --- a/addon/io_scs_tools/internals/open_gl/shaders/__init__.py +++ b/addon/io_scs_tools/internals/open_gl/shaders/__init__.py @@ -18,7 +18,6 @@ # Copyright (C) 2019: SCS Software -import os import gpu @@ -31,22 +30,6 @@ class ShaderTypes: """Simple dictonary holding shader instances by shader type. To prevent loading shader each time one requests it.""" -def __get_shader_data__(shader_filename): - """Loads and gets shader data from given filename. - - :param shader_filename: filename of shader file inside io_scs_tools/internals/open_gl/shaders - :type shader_filename: str - :return: shader data string from given glsl shader - :rtype: str - """ - shader_filepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), shader_filename) - - with open(shader_filepath, "r") as file: - shader_data_str = file.read() - - return shader_data_str - - def get_shader(shader_type): """Get GPU shader for given type. @@ -58,19 +41,129 @@ def get_shader(shader_type): if shader_type == ShaderTypes.SMOOTH_COLOR_CLIPPED_3D: if shader_type not in __cache: - __cache[shader_type] = gpu.types.GPUShader( - __get_shader_data__("smooth_color_clipped_3d_vert.glsl"), - __get_shader_data__("smooth_color_clipped_3d_frag.glsl") + vert_out = gpu.types.GPUStageInterfaceInfo("my_interface") + vert_out.smooth("VEC4", "finalColor") + vert_out.smooth("VEC4", "vertexPos") + + shader_info = gpu.types.GPUShaderCreateInfo() + shader_info.push_constant("MAT4", "ModelViewProjectionMatrix") + shader_info.push_constant("MAT4", "ModelMatrix") + + shader_info.typedef_source( + """ + struct ClipData { + vec4 clip_planes[6]; + int num_clip_planes; + int _pad1; + int _pad2; + int _pad3; + }; + """ + ) + shader_info.uniform_buf(0, "ClipData", "clip_data") + + shader_info.vertex_in(0, "VEC3", "pos") + shader_info.vertex_in(1, "VEC4", "color") + + shader_info.vertex_out(vert_out) + shader_info.fragment_out(0, "VEC4", "fragColor") + + shader_info.vertex_source( + """ + void main() + { + vertexPos = vec4(pos, 1.0); + gl_Position = ModelViewProjectionMatrix * vertexPos; + gl_PointSize = 12.0; + finalColor = color; + + #ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance((ModelMatrix * vec4(pos, 1.0)).xyz); + #endif + } + """ ) + shader_info.fragment_source( + """ + void main() + { + for (int i=0; i 0: + msg = ( + "\nWelcome folks. You just migrated to Blender 4.3+! Yey", + "Big thanks, that you decided to try my unofficial BT update. I appreciate it.", + "I hope you will enjoy new features and fixes I've added to this version.", + "For full changelog and more details, visit official topic on: https://www.forum.scssoft.com" + ) + + with bpy.context.temp_override(window=windows[0]): + bpy.ops.wm.scs_tools_show_3dview_report('INVOKE_DEFAULT', message="\n".join(msg)) + + +def apply_fixes_for_un_7(): + """ + Applies fixes for unofficial 2.4.7 or less: + 1. Reload materials since some got removed/restructed attributes + """ + + print("INFO\t- Applying fixes for unofficial versions < 7") + + # 1. reload all materials + # Some attributes got removed, in some cases attribute size changed and due to that we need to reload materials + _reload_materials() + +def apply_fixes_for_un_8(): + """ + Applies fixes for unofficial 2.4.8 or less: + 1. Reload materials since some got restructed nodes + 2. Show welcome message + """ + + print("INFO\t- Applying fixes for unofficial versions < 8") + + # 1. reload all materials + # Some shaders like nmaps got restructured nodes and due to that we need to reload materials + _reload_materials() + + # 2. Due to update to Blender 5.0+, we let user know + windows = bpy.data.window_managers[0].windows + if len(windows) > 0: + msg = ( + "\nWelcome folks. You just migrated to Blender 5.0+! Yey", + ) + + with bpy.context.temp_override(window=windows[0]): + bpy.ops.wm.scs_tools_show_3dview_report('INVOKE_DEFAULT', message="\n".join(msg)) diff --git a/addon/io_scs_tools/internals/preview_models/__init__.py b/addon/io_scs_tools/internals/preview_models/__init__.py index f08d803d..c80365ba 100644 --- a/addon/io_scs_tools/internals/preview_models/__init__.py +++ b/addon/io_scs_tools/internals/preview_models/__init__.py @@ -168,7 +168,7 @@ def load(locator, deep_reload=False): new_mesh = obj.data # set preview model path to mesh, so it can be reused next time user requests same preview model - new_mesh.scs_props.locator_preview_model_path = locator.scs_props.locator_preview_model_path + new_mesh.scs_props.locator_preview_model_path = _path_utils.readable_norm(abs_filepath) # now remove imported object, as we need only mesh bpy.data.objects.remove(obj, do_unlink=True) diff --git a/addon/io_scs_tools/internals/shaders/eut2/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/__init__.py index 973e4993..02c9e33d 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/__init__.py @@ -32,7 +32,7 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.none import NNone as Shader - elif effect == "water": + elif effect.startswith("water"): from io_scs_tools.internals.shaders.eut2.water import Water as Shader @@ -40,6 +40,14 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.window.lit import WindowLit as Shader + elif effect == "interior.lit" or effect == "interior.spatial.lit": + + from io_scs_tools.internals.shaders.eut2.interior import InteriorLit as Shader + + elif effect == "interior.curtain.lit": + + from io_scs_tools.internals.shaders.eut2.interior.curtain import InteriorCurtain as Shader + elif effect == "reflective": from io_scs_tools.internals.shaders.eut2.reflective import Reflective as Shader @@ -52,7 +60,11 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.grass import Grass as Shader - elif effect == "glass": + elif effect.startswith("leaves"): + + from io_scs_tools.internals.shaders.eut2.leaves import Leaves as Shader + + elif effect.startswith("glass"): from io_scs_tools.internals.shaders.eut2.glass import Glass as Shader @@ -68,6 +80,10 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.shadowonly import Shadowonly as Shader + elif effect.startswith("particle"): + + from io_scs_tools.internals.shaders.eut2.particle import Particle as Shader + elif effect.startswith("lightmap.night"): from io_scs_tools.internals.shaders.eut2.lightmap.night import LightMapNight as Shader @@ -76,6 +92,10 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.light_tex import LightTex as Shader + elif effect.startswith("light"): + + from io_scs_tools.internals.shaders.eut2.light import Light as Shader + elif effect.startswith("retroreflective"): from io_scs_tools.internals.shaders.eut2.retroreflective import Retroreflective as Shader @@ -127,15 +147,31 @@ def get_shader(effect): elif effect.startswith("decalshadow"): from io_scs_tools.internals.shaders.eut2.decalshadow import Decalshadow as Shader + + elif effect.startswith("dif.spec.over.dif.opac.add.env"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_over_dif_opac_add_env import DifSpecOverDifOpacAddEnv as Shader elif effect.startswith("dif.spec.over.dif.opac"): from io_scs_tools.internals.shaders.eut2.dif_spec_over_dif_opac import DifSpecOverDifOpac as Shader + + elif effect.startswith("dif.spec.amod.dif.spec.add.env"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_amod_dif_spec_add_env import DifSpecAmodDifSpecAddEnv as Shader + + elif effect.startswith("dif.spec.amod.dif.spec"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_amod_dif_spec import DifSpecAmodDifSpec as Shader elif effect.startswith("dif.spec.mult.dif.spec.iamod.dif.spec"): from io_scs_tools.internals.shaders.eut2.dif_spec_mult_dif_spec_iamod_dif_spec import DifSpecMultDifSpecIamodDifSpec as Shader + elif effect.startswith("dif.spec.mult.dif.iamod.dif.add.env"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_mult_dif_iamod_dif_add_env import DifSpecMultDifIamodDifAddEnv as Shader + elif effect.startswith("dif.spec.mult.dif.spec.add.env"): from io_scs_tools.internals.shaders.eut2.dif_spec_mult_dif_spec.add_env import DifSpecMultDifSpecAddEnv as Shader @@ -144,19 +180,29 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.dif_spec_mult_dif_spec import DifSpecMultDifSpec as Shader + elif effect.startswith("dif.spec.add.env.over.dif.opac"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_add_env_over_dif_opac import DifSpecAddEnvOverDifOpac as Shader + elif effect.startswith("dif.spec.add.env.nofresnel"): from io_scs_tools.internals.shaders.eut2.dif_spec_add_env.nofresnel import DifSpecAddEnvNoFresnel as Shader - elif effect.startswith("building.add.env.day"): + # Changed because of "lvcol.day" variant (all "buildings.add.env" use this same shader) + # + # elif effect.startswith("building.add.env.day"): + elif effect.startswith("building.add.env"): from io_scs_tools.internals.shaders.eut2.building.add_env_day import BuildingAddEnvDay as Shader - elif effect.startswith("building.lvcol.day"): - - from io_scs_tools.internals.shaders.eut2.building.lvcol_day import BuildingLvcolDay as Shader + # Changed because of "asafew.(...).day" variant (all non add.env "buildings" use this same shader) + # + # elif effect.startswith("building.lvcol.day"): + # + # from io_scs_tools.internals.shaders.eut2.building.lvcol_day import BuildingLvcolDay as Shader - elif effect.startswith("building.day"): + # elif effect.startswith("building.day"): + elif effect.startswith("building"): from io_scs_tools.internals.shaders.eut2.building.day import BuildingDay as Shader @@ -180,6 +226,10 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.dif_spec_oclu_weight_add_env import DifSpecOcluWeightAddEnv as Shader + elif effect.startswith("dif.spec.weight.add.env.nofresnel"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_weight_add_env.nofresnel import DifSpecWeightAddEnvNoFresnel as Shader + elif effect.startswith("dif.spec.weight.add.env"): from io_scs_tools.internals.shaders.eut2.dif_spec_weight_add_env import DifSpecWeightAddEnv as Shader @@ -188,10 +238,18 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.dif_spec_weight_weight_dif_spec_weight import DifSpecWeightWeightDifSpecWeight as Shader + elif effect.startswith("dif.spec.weight.mask.dif.spec.weight"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_weight_mask_dif_spec_weight import DifSpecWeightMaskDifSpecWeight as Shader + elif effect.startswith("dif.spec.weight.mult2.weight2"): from io_scs_tools.internals.shaders.eut2.dif_spec_weight_mult2_weight2 import DifSpecWeightMult2Weight2 as Shader + elif effect.startswith("dif.spec.weight.mult2.mask2"): + + from io_scs_tools.internals.shaders.eut2.dif_spec_weight_mult2_mask2 import DifSpecWeightMult2Mask2 as Shader + elif effect.startswith("dif.spec.weight.mult2"): from io_scs_tools.internals.shaders.eut2.dif_spec_weight_mult2 import DifSpecWeightMult2 as Shader @@ -224,6 +282,24 @@ def get_shader(effect): from io_scs_tools.internals.shaders.eut2.dif import Dif as Shader + elif effect.startswith("billboard"): + + from io_scs_tools.internals.shaders.eut2.billboard import Billboard as Shader + + elif effect.startswith("baked.spec"): + + if ".add.env" in effect: + + from io_scs_tools.internals.shaders.eut2.baked.add_env import BakedSpecAddEnv as Shader + + else: + + from io_scs_tools.internals.shaders.eut2.baked.spec import BakedSpec as Shader + + elif effect.startswith("baked"): + + from io_scs_tools.internals.shaders.eut2.baked import Baked as Shader + else: return None diff --git a/addon/io_scs_tools/internals/shaders/eut2/baked/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/baked/__init__.py new file mode 100644 index 00000000..2f4ea0f0 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/baked/__init__.py @@ -0,0 +1,265 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2024: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.base import BaseShader +from io_scs_tools.internals.shaders.eut2.std_node_groups import compose_lighting_ng +from io_scs_tools.internals.shaders.eut2.std_node_groups import lighting_evaluator_ng +from io_scs_tools.internals.shaders.eut2.std_node_groups import vcolor_input_ng +from io_scs_tools.internals.shaders.flavors import nmap +from io_scs_tools.utils import convert as _convert_utils +from io_scs_tools.utils import material as _material_utils + + +class Baked(BaseShader): + GEOM_NODE = "Geometry" + UVMAP_NODE = "FirstUVs" + VCOL_GROUP_NODE = "VColorGroup" + BASE_TEX_NODE = "BaseTex" + VCOLOR_MULT_NODE = "VertexColorMultiplier" + VCOLOR_SCALE_NODE = "VertexColorScale" + LIGHTING_EVAL_NODE = "LightingEvaluator" + COMPOSE_LIGHTING_NODE = "ComposeLighting" + OUTPUT_NODE = "Output" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # node creation + vcol_group_n = node_tree.nodes.new("ShaderNodeGroup") + vcol_group_n.name = Baked.VCOL_GROUP_NODE + vcol_group_n.label = Baked.VCOL_GROUP_NODE + vcol_group_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1650) + vcol_group_n.node_tree = vcolor_input_ng.get_node_group() + + uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") + uvmap_n.name = Baked.UVMAP_NODE + uvmap_n.label = Baked.UVMAP_NODE + uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1500) + uvmap_n.uv_map = _MESH_consts.none_uv + + geometry_n = node_tree.nodes.new("ShaderNodeNewGeometry") + geometry_n.name = Baked.GEOM_NODE + geometry_n.label = Baked.GEOM_NODE + geometry_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1350) + + vcol_scale_n = node_tree.nodes.new("ShaderNodeVectorMath") + vcol_scale_n.name = Baked.VCOLOR_SCALE_NODE + vcol_scale_n.label = Baked.VCOLOR_SCALE_NODE + vcol_scale_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1550) + vcol_scale_n.operation = "MULTIPLY" + vcol_scale_n.inputs[1].default_value = (2,) * 3 + + base_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + base_tex_n.name = Baked.BASE_TEX_NODE + base_tex_n.label = Baked.BASE_TEX_NODE + base_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1500) + base_tex_n.width = 140 + + vcol_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + vcol_mult_n.name = Baked.VCOLOR_MULT_NODE + vcol_mult_n.label = Baked.VCOLOR_MULT_NODE + vcol_mult_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1500) + vcol_mult_n.operation = "MULTIPLY" + + lighting_eval_n = node_tree.nodes.new("ShaderNodeGroup") + lighting_eval_n.name = Baked.LIGHTING_EVAL_NODE + lighting_eval_n.label = Baked.LIGHTING_EVAL_NODE + lighting_eval_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 1800) + lighting_eval_n.node_tree = lighting_evaluator_ng.get_node_group() + + compose_lighting_n = node_tree.nodes.new("ShaderNodeGroup") + compose_lighting_n.name = Baked.COMPOSE_LIGHTING_NODE + compose_lighting_n.label = Baked.COMPOSE_LIGHTING_NODE + compose_lighting_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 2000) + compose_lighting_n.node_tree = compose_lighting_ng.get_node_group() + compose_lighting_n.inputs["Alpha"].default_value = 1.0 + + output_n = node_tree.nodes.new("ShaderNodeOutputMaterial") + output_n.name = Baked.OUTPUT_NODE + output_n.label = Baked.OUTPUT_NODE + output_n.location = (start_pos_x + pos_x_shift * 9, start_pos_y + 1800) + + # links creation + node_tree.links.new(base_tex_n.inputs['Vector'], uvmap_n.outputs['UV']) + node_tree.links.new(vcol_scale_n.inputs[0], vcol_group_n.outputs['Vertex Color']) + + node_tree.links.new(vcol_mult_n.inputs[0], vcol_scale_n.outputs[0]) + node_tree.links.new(vcol_mult_n.inputs[1], base_tex_n.outputs['Color']) + + node_tree.links.new(lighting_eval_n.inputs['Normal Vector'], geometry_n.outputs['Normal']) + node_tree.links.new(lighting_eval_n.inputs['Incoming Vector'], geometry_n.outputs['Incoming']) + + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], vcol_mult_n.outputs[0]) + node_tree.links.new(compose_lighting_n.inputs['Specular Lighting'], lighting_eval_n.outputs['Specular Lighting']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Lighting'], lighting_eval_n.outputs['Diffuse Lighting']) + + node_tree.links.new(output_n.inputs['Surface'], compose_lighting_n.outputs['Shader']) + + @staticmethod + def finalize(node_tree, material): + """Finalize node tree and material settings. Should be called as last. + + :param node_tree: node tree on which this shader should be finalized + :type node_tree: bpy.types.NodeTree + :param material: material used for this shader + :type material: bpy.types.Material + """ + + material.use_backface_culling = True + material.surface_render_method = "DITHERED" + + @staticmethod + def set_shadow_bias(node_tree, value): + """Set shadow bias attirbute for this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param value: shador bias factor + :type value: float + """ + + pass # NOTE: shadow bias won't be visualized as game uses it's own implementation + + @staticmethod + def set_queue_bias(node_tree, value): + """Set queue bias attirbute for this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param value: queue bias index + :type value: int + """ + + pass # NOTE: shadow bias won't be visualized as game uses it's own implementation + + @staticmethod + def set_base_texture(node_tree, image): + """Set base texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to base texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[Baked.BASE_TEX_NODE].image = image + + @staticmethod + def set_base_texture_settings(node_tree, settings): + """Set base texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Baked.BASE_TEX_NODE], settings) + + @staticmethod + def set_base_uv(node_tree, uv_layer): + """Set UV layer to base texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[Baked.UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_nmap_flavor(node_tree, switch_on): + """Set normal map flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if normal map should be switched on or off + :type switch_on: bool + """ + + if switch_on: + + # find minimal y position for input nodes and position flavor beneath it + min_y = None + for node in node_tree.nodes: + if node.location.x <= 185 and (min_y is None or min_y > node.location.y): + min_y = node.location.y + + lighting_eval_n = node_tree.nodes[Baked.LIGHTING_EVAL_NODE] + geom_n = node_tree.nodes[Baked.GEOM_NODE] + location = (lighting_eval_n.location.x - 185, min_y - 400) + + nmap.init(node_tree, location, lighting_eval_n.inputs['Normal Vector'], geom_n.outputs['Normal']) + else: + nmap.delete(node_tree) + + @staticmethod + def set_nmap_texture(node_tree, image): + """Set normal map texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to nmap texture node + :type image: bpy.types.Image + """ + + nmap.set_texture(node_tree, image) + + @staticmethod + def set_nmap_texture_settings(node_tree, settings): + """Set normal map texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + nmap.set_texture_settings(node_tree, settings) + + @staticmethod + def set_nmap_uv(node_tree, uv_layer): + """Set UV layer to normal map texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + nmap.set_uv(node_tree, uv_layer) diff --git a/addon/io_scs_tools/internals/shaders/eut2/baked/add_env.py b/addon/io_scs_tools/internals/shaders/eut2/baked/add_env.py new file mode 100644 index 00000000..2e5822cf --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/baked/add_env.py @@ -0,0 +1,230 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2024: SCS Software + +from io_scs_tools.internals.shaders.eut2.baked.spec import BakedSpec +from io_scs_tools.internals.shaders.eut2.parameters import get_fresnel_truckpaint +from io_scs_tools.internals.shaders.eut2.std_node_groups import linear_to_srgb_ng +from io_scs_tools.internals.shaders.eut2.std_passes.add_env import StdAddEnv +from io_scs_tools.utils import convert as _convert_utils +from io_scs_tools.utils import material as _material_utils +from io_scs_tools.utils import get_scs_globals as _get_scs_globals + +class BakedSpecAddEnv(BakedSpec, StdAddEnv): + MASK_TEX_NODE = "MaskTex" + MASK1_TEX_NODE = "Mask1Tex" + + BASE_PAINT_MULT_NODE = "BasePaintMult" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + BakedSpec.init(node_tree) + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + BakedSpec.init(node_tree) + + + StdAddEnv.add(node_tree, + BakedSpec.GEOM_NODE, + None, + None, + node_tree.nodes[BakedSpec.LIGHTING_EVAL_NODE].outputs['Normal'], + node_tree.nodes[BakedSpec.COMPOSE_LIGHTING_NODE].inputs['Env Color']) + + uvmap_n = node_tree.nodes[BakedSpec.UVMAP_NODE] + + add_env_gn = node_tree.nodes[StdAddEnv.ADD_ENV_GROUP_NODE] + + # overrides + # set strength multiplier to 2 for better visualization in Blender + node_tree.nodes[StdAddEnv.ADD_ENV_GROUP_NODE].inputs['Strength Multiplier'].default_value = 2.0 + + # node creation + mask_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_tex_n.name = mask_tex_n.label = BakedSpecAddEnv.MASK_TEX_NODE + mask_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 2200) + mask_tex_n.width = 140 + + # links creation + node_tree.links.new(uvmap_n.outputs['UV'], mask_tex_n.inputs['Vector']) + + node_tree.links.new(mask_tex_n.outputs['Color'], add_env_gn.inputs['Env Factor Color']) + + @staticmethod + def init_paint(node_tree): + """Initialize extended node tree for colormask or airbrush flavors with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # make sure to skip execution if node exists + if BakedSpecAddEnv.MASK1_TEX_NODE not in node_tree.nodes: + + uvmap_n = node_tree.nodes[BakedSpec.UVMAP_NODE] + vcol_mult_n = node_tree.nodes[BakedSpec.VCOLOR_MULT_NODE] + compose_lighting_n = node_tree.nodes[BakedSpec.COMPOSE_LIGHTING_NODE] + + # node creation + mask1_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask1_tex_n.name = mask1_tex_n.label = BakedSpecAddEnv.MASK1_TEX_NODE + mask1_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1150) + mask1_tex_n.width = 140 + + base_paint_mult_n = node_tree.nodes.new("ShaderNodeMix") + base_paint_mult_n.name = base_paint_mult_n.label = BakedSpecAddEnv.BASE_PAINT_MULT_NODE + base_paint_mult_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 1500) + base_paint_mult_n.data_type = "RGBA" + base_paint_mult_n.blend_type = "MULTIPLY" + base_paint_mult_n.inputs['Factor'].default_value = 1 + base_paint_mult_n.inputs['B'].default_value = _convert_utils.to_node_color(_get_scs_globals().base_paint_color) + + # links creation + node_tree.links.new(uvmap_n.outputs['UV'], mask1_tex_n.inputs['Vector']) + node_tree.links.new(mask1_tex_n.outputs['Color'], base_paint_mult_n.inputs['Factor']) + node_tree.links.new(vcol_mult_n.outputs['Vector'], base_paint_mult_n.inputs['A']) + node_tree.links.new(base_paint_mult_n.outputs['Result'], compose_lighting_n.inputs['Diffuse Color']) + + + @staticmethod + def set_mask_texture(node_tree, image): + """Set mask texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to mask texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[BakedSpecAddEnv.MASK_TEX_NODE].image = image + + @staticmethod + def set_mask_texture_settings(node_tree, settings): + """Set mask texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[BakedSpecAddEnv.MASK_TEX_NODE], settings) + + @staticmethod + def set_mask_uv(node_tree, uv_layer): + """Set UV layer to mask texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mask texture + :type uv_layer: str + """ + + BakedSpec.set_base_uv(node_tree, uv_layer) + + @staticmethod + def set_mask_1_texture(node_tree, image): + """Set mask 1 texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mask 1 texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[BakedSpecAddEnv.MASK1_TEX_NODE].image = image + + @staticmethod + def set_mask_1_texture_settings(node_tree, settings): + """Set mask 1 texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[BakedSpecAddEnv.MASK1_TEX_NODE], settings) + + @staticmethod + def set_mask_1_uv(node_tree, uv_layer): + """Set UV layer to mask 1 texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mask 1 texture + :type uv_layer: str + """ + + BakedSpec.set_base_uv(node_tree, uv_layer) + + @staticmethod + def set_paint_flavor(node_tree, switch_on): + """Set paint flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + if switch_on: + BakedSpecAddEnv.init_paint(node_tree) + + @staticmethod + def set_fresnel(node_tree, bias_scale): + """Set fresnel bias and scale value to shader. + More tests are needed to determine if this is correct for paint flavor in baked shader, because without that, model is too glossy. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param bias_scale: bias and scale factors as tuple: (bias, scale) + :type bias_scale: (float, float) + """ + # skip ecxecution if node for "paint" flavor does not exist + if BakedSpecAddEnv.MASK1_TEX_NODE not in node_tree.nodes: + return + + bias_scale_truckpaint = get_fresnel_truckpaint(bias_scale[0], bias_scale[1]) + StdAddEnv.set_fresnel(node_tree, bias_scale_truckpaint) + + node_tree.nodes[StdAddEnv.ADD_ENV_GROUP_NODE].inputs['Fresnel Type'].default_value = 1 + + \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/baked/spec.py b/addon/io_scs_tools/internals/shaders/eut2/baked/spec.py new file mode 100644 index 00000000..5d1dc3a9 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/baked/spec.py @@ -0,0 +1,114 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2024: SCS Software + +from io_scs_tools.internals.shaders.eut2.baked import Baked +from io_scs_tools.internals.shaders.eut2.std_node_groups import spec_texture_calc_ng +from io_scs_tools.utils import material as _material_utils + +class BakedSpec(Baked): + OVER_TEX_NODE = "OverTex" + SPEC_TEX_CALC = "SpecTexCalc" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + Baked.init(node_tree) + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + Baked.init(node_tree) + + uvmap_n = node_tree.nodes[Baked.UVMAP_NODE] + lighting_eval_n = node_tree.nodes[Baked.LIGHTING_EVAL_NODE] + compose_lighting_n = node_tree.nodes[Baked.COMPOSE_LIGHTING_NODE] + + # node creation + over_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_tex_n.name = BakedSpec.OVER_TEX_NODE + over_tex_n.label = BakedSpec.OVER_TEX_NODE + over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1900) + over_tex_n.width = 140 + + over_tex_calc_ng = node_tree.nodes.new("ShaderNodeGroup") + over_tex_calc_ng.name = BakedSpec.SPEC_TEX_CALC + over_tex_calc_ng.label = BakedSpec.SPEC_TEX_CALC + over_tex_calc_ng.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 1900) + over_tex_calc_ng.node_tree = spec_texture_calc_ng.get_node_group() + + + # links creation + node_tree.links.new(uvmap_n.outputs['UV'], over_tex_n.inputs['Vector']) + + node_tree.links.new(over_tex_n.outputs['Color'], over_tex_calc_ng.inputs['Color']) + + node_tree.links.new(over_tex_calc_ng.outputs['Shininess'], lighting_eval_n.inputs['Shininess']) + node_tree.links.new(over_tex_calc_ng.outputs['Specular'], compose_lighting_n.inputs['Specular Color']) + + + @staticmethod + def set_over_texture(node_tree, image): + """Set over texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to over texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[BakedSpec.OVER_TEX_NODE].image = image + + @staticmethod + def set_over_texture_settings(node_tree, settings): + """Set over texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[BakedSpec.OVER_TEX_NODE], settings) + + @staticmethod + def set_over_uv(node_tree, uv_layer): + """Set UV layer to over texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + Baked.set_base_uv(node_tree, uv_layer) \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/billboard/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/billboard/__init__.py new file mode 100644 index 00000000..08fcae70 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/billboard/__init__.py @@ -0,0 +1,416 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.base import BaseShader +from io_scs_tools.internals.shaders.eut2.std_node_groups import alpha_remap_ng +from io_scs_tools.internals.shaders.eut2.std_node_groups import compose_lighting_ng +from io_scs_tools.internals.shaders.eut2.std_node_groups import lighting_evaluator_ng +from io_scs_tools.internals.shaders.eut2.std_node_groups import vcolor_input_ng +from io_scs_tools.internals.shaders.flavors import nmap +from io_scs_tools.utils import convert as _convert_utils +from io_scs_tools.utils import material as _material_utils +from io_scs_tools.internals.shaders.flavors import asafew + + +class Billboard(BaseShader): + DIFF_COL_NODE = "DiffuseColor" + SPEC_COL_NODE = "SpecularColor" + GEOM_NODE = "Geometry" + UVMAP_NODE = "FirstUVs" + VCOL_GROUP_NODE = "VColorGroup" + BASE_TEX_NODE = "BaseTex" + DIFF_MULT_NODE = "DiffMultiplier" + SPEC_MULT_NODE = "SpecMultiplier" + VCOLOR_MULT_NODE = "VertexColorMultiplier" + VCOLOR_SCALE_NODE = "VertexColorScale" + REMAP_ALPHA_GNODE = "RemapAlphaToWeight" + LIGHTING_EVAL_NODE = "LightingEvaluator" + COMPOSE_LIGHTING_NODE = "ComposeLighting" + OUTPUT_NODE = "Output" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree, disable_remap_alpha=False): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # node creation + vcol_group_n = node_tree.nodes.new("ShaderNodeGroup") + vcol_group_n.name = Billboard.VCOL_GROUP_NODE + vcol_group_n.label = Billboard.VCOL_GROUP_NODE + vcol_group_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1650) + vcol_group_n.node_tree = vcolor_input_ng.get_node_group() + + uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") + uvmap_n.name = Billboard.UVMAP_NODE + uvmap_n.label = Billboard.UVMAP_NODE + uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1500) + uvmap_n.uv_map = _MESH_consts.none_uv + + geometry_n = node_tree.nodes.new("ShaderNodeNewGeometry") + geometry_n.name = Billboard.GEOM_NODE + geometry_n.label = Billboard.GEOM_NODE + geometry_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1350) + + diff_col_n = node_tree.nodes.new("ShaderNodeRGB") + diff_col_n.name = Billboard.DIFF_COL_NODE + diff_col_n.label = Billboard.DIFF_COL_NODE + diff_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1700) + + spec_col_n = node_tree.nodes.new("ShaderNodeRGB") + spec_col_n.name = Billboard.SPEC_COL_NODE + spec_col_n.label = Billboard.SPEC_COL_NODE + spec_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1900) + + spec_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + spec_mult_n.name = Billboard.SPEC_MULT_NODE + spec_mult_n.label = Billboard.SPEC_MULT_NODE + spec_mult_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1900) + spec_mult_n.operation = "MULTIPLY" + + vcol_scale_n = node_tree.nodes.new("ShaderNodeVectorMath") + vcol_scale_n.name = Billboard.VCOLOR_SCALE_NODE + vcol_scale_n.label = Billboard.VCOLOR_SCALE_NODE + vcol_scale_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1550) + vcol_scale_n.operation = "MULTIPLY" + vcol_scale_n.inputs[1].default_value = (2,) * 3 + + base_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + base_tex_n.name = Billboard.BASE_TEX_NODE + base_tex_n.label = Billboard.BASE_TEX_NODE + base_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1500) + base_tex_n.width = 140 + + vcol_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + vcol_mult_n.name = Billboard.VCOLOR_MULT_NODE + vcol_mult_n.label = Billboard.VCOLOR_MULT_NODE + vcol_mult_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1500) + vcol_mult_n.operation = "MULTIPLY" + + diff_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + diff_mult_n.name = Billboard.DIFF_MULT_NODE + diff_mult_n.label = Billboard.DIFF_MULT_NODE + diff_mult_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 1650) + diff_mult_n.operation = "MULTIPLY" + diff_mult_n.inputs[1].default_value = (0, 0, 0) + + lighting_eval_n = node_tree.nodes.new("ShaderNodeGroup") + lighting_eval_n.name = Billboard.LIGHTING_EVAL_NODE + lighting_eval_n.label = Billboard.LIGHTING_EVAL_NODE + lighting_eval_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 1800) + lighting_eval_n.node_tree = lighting_evaluator_ng.get_node_group() + + compose_lighting_n = node_tree.nodes.new("ShaderNodeGroup") + compose_lighting_n.name = Billboard.COMPOSE_LIGHTING_NODE + compose_lighting_n.label = Billboard.COMPOSE_LIGHTING_NODE + compose_lighting_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 2000) + compose_lighting_n.node_tree = compose_lighting_ng.get_node_group() + compose_lighting_n.inputs["Alpha"].default_value = 1.0 + compose_lighting_n.inputs["Alpha Type"].default_value = -1.0 + + output_n = node_tree.nodes.new("ShaderNodeOutputMaterial") + output_n.name = Billboard.OUTPUT_NODE + output_n.label = Billboard.OUTPUT_NODE + output_n.location = (start_pos_x + pos_x_shift * 9, start_pos_y + 1800) + + remap_alpha_n = None + if not disable_remap_alpha: + remap_alpha_n = node_tree.nodes.new("ShaderNodeGroup") + remap_alpha_n.name = remap_alpha_n.label = Billboard.REMAP_ALPHA_GNODE + remap_alpha_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 1900) + remap_alpha_n.node_tree = alpha_remap_ng.get_node_group() + remap_alpha_n.inputs['Factor1'].default_value = 1.0 + remap_alpha_n.inputs['Factor2'].default_value = 0.0 + + # links creation + node_tree.links.new(base_tex_n.inputs['Vector'], uvmap_n.outputs['UV']) + node_tree.links.new(vcol_scale_n.inputs[0], vcol_group_n.outputs['Vertex Color']) + + node_tree.links.new(vcol_mult_n.inputs[0], vcol_scale_n.outputs[0]) + node_tree.links.new(vcol_mult_n.inputs[1], base_tex_n.outputs['Color']) + + node_tree.links.new(diff_mult_n.inputs[0], diff_col_n.outputs['Color']) + node_tree.links.new(diff_mult_n.inputs[1], vcol_mult_n.outputs[0]) + + node_tree.links.new(lighting_eval_n.inputs['Normal Vector'], geometry_n.outputs['Normal']) + node_tree.links.new(lighting_eval_n.inputs['Incoming Vector'], geometry_n.outputs['Incoming']) + + node_tree.links.new(compose_lighting_n.inputs['Specular Color'], spec_col_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], diff_mult_n.outputs[0]) + node_tree.links.new(compose_lighting_n.inputs['Specular Lighting'], lighting_eval_n.outputs['Specular Lighting']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Lighting'], lighting_eval_n.outputs['Diffuse Lighting']) + node_tree.links.new(compose_lighting_n.inputs['Alpha'], base_tex_n.outputs["Alpha"]) + + node_tree.links.new(output_n.inputs['Surface'], compose_lighting_n.outputs['Shader']) + + if not disable_remap_alpha: + node_tree.links.new(remap_alpha_n.inputs['Alpha'], base_tex_n.outputs['Alpha']) + node_tree.links.new(spec_mult_n.inputs[1], remap_alpha_n.outputs['Weighted Alpha']) + else: + node_tree.links.new(spec_mult_n.inputs[1], base_tex_n.outputs['Alpha']) + + node_tree.links.new(spec_mult_n.inputs[0], spec_col_n.outputs['Color']) + + node_tree.links.new(compose_lighting_n.inputs['Specular Color'], spec_mult_n.outputs[0]) + + @staticmethod + def finalize(node_tree, material): + """Finalize node tree and material settings. Should be called as last. + + :param node_tree: node tree on which this shader should be finalized + :type node_tree: bpy.types.NodeTree + :param material: material used for this shader + :type material: bpy.types.Material + """ + + compose_lighting_n = node_tree.nodes[Billboard.COMPOSE_LIGHTING_NODE] + + # Unique shader - billboard shader do not use backface culling + material.use_backface_culling = False + material.surface_render_method = "DITHERED" + compose_lighting_n.inputs["Alpha Type"].default_value = 0.0 + + if compose_lighting_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[Billboard.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: + node_tree.links.remove(node_tree.nodes[Billboard.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links[0]) + + @staticmethod + def set_add_ambient(node_tree, factor): + """Set ambient factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param factor: add ambient factor + :type factor: float + """ + + node_tree.nodes[Billboard.COMPOSE_LIGHTING_NODE].inputs["AddAmbient"].default_value = factor + + @staticmethod + def set_diffuse(node_tree, color): + """Set diffuse color to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param color: diffuse color + :type color: Color or tuple + """ + + color = _convert_utils.to_node_color(color) + + node_tree.nodes[Billboard.DIFF_COL_NODE].outputs['Color'].default_value = color + + @staticmethod + def set_specular(node_tree, color): + """Set specular color to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param color: specular color + :type color: Color or tuple + """ + + color = _convert_utils.to_node_color(color) + + node_tree.nodes[Billboard.SPEC_COL_NODE].outputs['Color'].default_value = color + + @staticmethod + def set_shininess(node_tree, factor): + """Set shininess factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param factor: shininess factor + :type factor: float + """ + + node_tree.nodes[Billboard.LIGHTING_EVAL_NODE].inputs["Shininess"].default_value = factor + + @staticmethod + def set_shadow_bias(node_tree, value): + """Set shadow bias attirbute for this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param value: shador bias factor + :type value: float + """ + + pass # NOTE: shadow bias won't be visualized as game uses it's own implementation + + @staticmethod + def set_base_texture(node_tree, image): + """Set base texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to base texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[Billboard.BASE_TEX_NODE].image = image + + @staticmethod + def set_base_texture_settings(node_tree, settings): + """Set base texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Billboard.BASE_TEX_NODE], settings) + + @staticmethod + def set_base_uv(node_tree, uv_layer): + """Set UV layer to base texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[Billboard.UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_nmap_flavor(node_tree, switch_on): + """Set normal map flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if normal map should be switched on or off + :type switch_on: bool + """ + + if switch_on: + + # find minimal y position for input nodes and position flavor beneath it + min_y = None + for node in node_tree.nodes: + if node.location.x <= 185 and (min_y is None or min_y > node.location.y): + min_y = node.location.y + + lighting_eval_n = node_tree.nodes[Billboard.LIGHTING_EVAL_NODE] + geom_n = node_tree.nodes[Billboard.GEOM_NODE] + location = (lighting_eval_n.location.x - 185, min_y - 400) + + nmap.init(node_tree, location, lighting_eval_n.inputs['Normal Vector'], geom_n.outputs['Normal']) + else: + nmap.delete(node_tree) + + @staticmethod + def set_nmap_texture(node_tree, image): + """Set normal map texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to nmap texture node + :type image: bpy.types.Image + """ + + nmap.set_texture(node_tree, image) + + @staticmethod + def set_nmap_texture_settings(node_tree, settings): + """Set normal map texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + nmap.set_texture_settings(node_tree, settings) + + @staticmethod + def set_nmap_uv(node_tree, uv_layer): + """Set UV layer to normal map texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + nmap.set_uv(node_tree, uv_layer) + + @staticmethod + def set_flat_flavor(node_tree, switch_on): + """Set flat shading flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + lighting_eval_n = node_tree.nodes[Billboard.LIGHTING_EVAL_NODE] + + if switch_on: + lighting_eval_n.inputs["Flat Lighting"].default_value = 1.0 + else: + lighting_eval_n.inputs["Flat Lighting"].default_value = 0.0 + + @staticmethod + def set_asafew_flavor(node_tree, switch_on): + """Set alpha test safe weight flavor to this shader. + + NOTE: there is no safety check if remap was enabled on initialization + thus calling this setter can result in error. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + remap_alpha_n = node_tree.nodes[Billboard.REMAP_ALPHA_GNODE] + + if switch_on: + asafew.init(node_tree, remap_alpha_n) + else: + asafew.delete(node_tree, remap_alpha_n) + + @staticmethod + def set_vertex_pox_mapping(node_tree, uv_layer): + """Set Vertex Position UV layer to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base texture + :type uv_layer: str + """ + + pass # NOTE: vertex position is not supported in this shader. \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py index 1056dcb6..7ef30b83 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif/__init__.py @@ -30,6 +30,7 @@ from io_scs_tools.internals.shaders.flavors import nmap from io_scs_tools.internals.shaders.flavors import paint from io_scs_tools.internals.shaders.flavors import tg0 +from io_scs_tools.internals.shaders.flavors import oinv from io_scs_tools.utils import convert as _convert_utils from io_scs_tools.utils import material as _material_utils @@ -139,6 +140,7 @@ def init(node_tree): compose_lighting_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 2000) compose_lighting_n.node_tree = compose_lighting_ng.get_node_group() compose_lighting_n.inputs["Alpha"].default_value = 1.0 + compose_lighting_n.inputs["Alpha Type"].default_value = -1.0 output_n = node_tree.nodes.new("ShaderNodeOutputMaterial") output_n.name = Dif.OUTPUT_NODE @@ -179,16 +181,16 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] # set proper blend method and possible alpha test pass if alpha_test.is_set(node_tree): - material.blend_method = "CLIP" - material.alpha_threshold = 0.05 + compose_lighting_n.inputs["Alpha Type"].default_value = 0.0 # add alpha test pass if multiply blend enabled, where alphed pixels shouldn't be multiplied as they are discarded if blend_mult.is_set(node_tree): - compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] # alpha test pass has to get fully opaque input, thus remove transparency linkage if compose_lighting_n.inputs['Alpha'].links: @@ -201,13 +203,16 @@ def finalize(node_tree, material): alpha_test.add_pass(node_tree, shader_from, alpha_from, shader_to) if blend_add.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_mult.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_over.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" - if material.blend_method == "OPAQUE" and node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: + if compose_lighting_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links[0]) @staticmethod @@ -501,7 +506,7 @@ def set_tg0_flavor(node_tree, switch_on): @staticmethod def set_aux0(node_tree, aux_property): - """Set zero texture generation scale. + """Set zero texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -510,8 +515,11 @@ def set_aux0(node_tree, aux_property): """ if tg0.is_set(node_tree): - - tg0.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + # Fix for old float2 aux[0] + if (len(aux_property)) == 2: + tg0.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg0.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) @staticmethod def set_flat_flavor(node_tree, switch_on): @@ -550,3 +558,24 @@ def set_paint_flavor(node_tree, switch_on): else: paint.delete(node_tree) + + @staticmethod + def set_oinv_flavor(node_tree, switch_on): + """Set oinv flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + opacity_n = node_tree.nodes[Dif.OPACITY_NODE] + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] + + if switch_on: + + location = (opacity_n.location.x + 185, opacity_n.location.y + 40) + oinv.init(node_tree, location, opacity_n.outputs["Value"], compose_lighting_n.inputs["Alpha"]) + + else: + oinv.delete(node_tree) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_anim/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_anim/__init__.py index 4c81719f..6ad932da 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_anim/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_anim/__init__.py @@ -21,6 +21,7 @@ from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.eut2.dif import Dif from io_scs_tools.internals.shaders.eut2.dif_anim import anim_blend_factor_ng +from io_scs_tools.internals.shaders.eut2.dif_anim import over_nmap from io_scs_tools.internals.shaders.flavors import fadesheet from io_scs_tools.internals.shaders.flavors import flipsheet from io_scs_tools.utils import material as _material_utils @@ -84,14 +85,16 @@ def init(node_tree): over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1000) over_tex_n.width = 140 - base_over_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_over_mix_n = node_tree.nodes.new("ShaderNodeMix") base_over_mix_n.name = base_over_mix_n.label = DifAnim.BASE_OVER_MIX_NODE base_over_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1300) + base_over_mix_n.data_type = "RGBA" base_over_mix_n.blend_type = "MIX" - base_over_a_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_over_a_mix_n = node_tree.nodes.new("ShaderNodeMix") base_over_a_mix_n.name = base_over_a_mix_n.label = DifAnim.BASE_OVER_AMIX_NODE - base_over_a_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1100) + base_over_a_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1050) + base_over_a_mix_n.data_type = "RGBA" base_over_a_mix_n.blend_type = "MIX" # links creation @@ -99,18 +102,18 @@ def init(node_tree): node_tree.links.new(over_tex_n.inputs['Vector'], sec_uvmap_n.outputs['UV']) # pass 1 - node_tree.links.new(base_over_mix_n.inputs['Fac'], blend_fac_gn.outputs['Factor']) - node_tree.links.new(base_over_mix_n.inputs['Color1'], base_tex_n.outputs['Color']) - node_tree.links.new(base_over_mix_n.inputs['Color2'], over_tex_n.outputs['Color']) + node_tree.links.new(base_over_mix_n.inputs['Factor'], blend_fac_gn.outputs['Factor']) + node_tree.links.new(base_over_mix_n.inputs['A'], base_tex_n.outputs['Color']) + node_tree.links.new(base_over_mix_n.inputs['B'], over_tex_n.outputs['Color']) - node_tree.links.new(base_over_a_mix_n.inputs['Fac'], blend_fac_gn.outputs['Factor']) - node_tree.links.new(base_over_a_mix_n.inputs['Color1'], base_tex_n.outputs['Alpha']) - node_tree.links.new(base_over_a_mix_n.inputs['Color2'], over_tex_n.outputs['Alpha']) + node_tree.links.new(base_over_a_mix_n.inputs['Factor'], blend_fac_gn.outputs['Factor']) + node_tree.links.new(base_over_a_mix_n.inputs['A'], base_tex_n.outputs['Alpha']) + node_tree.links.new(base_over_a_mix_n.inputs['B'], over_tex_n.outputs['Alpha']) # pass 2 - node_tree.links.new(vcol_mult_n.inputs[1], base_over_mix_n.outputs['Color']) + node_tree.links.new(vcol_mult_n.inputs[1], base_over_mix_n.outputs['Result']) - node_tree.links.new(opacity_n.inputs[0], base_over_a_mix_n.outputs['Color']) + node_tree.links.new(opacity_n.inputs[0], base_over_a_mix_n.outputs['Result']) @staticmethod def finalize(node_tree, material): @@ -139,6 +142,69 @@ def finalize(node_tree, material): node_tree.links.new(node_tree.nodes[DifAnim.VCOLOR_MULT_NODE].inputs[1], node_tree.nodes[DifAnim.BASE_TEX_NODE].outputs['Color']) node_tree.links.new(node_tree.nodes[DifAnim.OPACITY_NODE].inputs[0], node_tree.nodes[DifAnim.BASE_TEX_NODE].outputs['Alpha']) + + @staticmethod + def set_nmap_flavor(node_tree, switch_on): + """Set normal map flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if normal map should be switched on or off + :type switch_on: bool + """ + + if switch_on: + + # find minimal y position for input nodes and position flavor beneath it + min_y = None + for node in node_tree.nodes: + if node.location.x <= 185 and (min_y is None or min_y > node.location.y): + min_y = node.location.y + + lighting_eval_n = node_tree.nodes[DifAnim.LIGHTING_EVAL_NODE] + geom_n = node_tree.nodes[DifAnim.GEOM_NODE] + vcol_group_n = node_tree.nodes[DifAnim.VCOL_GROUP_NODE] + location = (lighting_eval_n.location.x - 185, min_y - 400) + + over_nmap.init(node_tree, location, lighting_eval_n.inputs['Normal Vector'], geom_n.outputs['Normal'], vcol_group_n.outputs["Vertex Color Alpha"]) + else: + over_nmap.delete(node_tree) + + @staticmethod + def set_nmap_over_uv(node_tree, uv_layer): + """Set UV layer to over normal map texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over nmap texture + :type uv_layer: str + """ + + over_nmap.set_over_uv(node_tree, uv_layer) + + @staticmethod + def set_nmap_over_texture(node_tree, texture): + """Set over normal map texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param texture: texture which should be assigned to over nmap texture node + :type texture: bpy.types.Texture + """ + + over_nmap.set_over_texture(node_tree, texture) + + @staticmethod + def set_nmap_over_texture_settings(node_tree, settings): + """Set over normal map texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + over_nmap.set_over_texture_settings(node_tree, settings) + @staticmethod def set_base_texture(node_tree, image): """Set base texture to shader. @@ -300,8 +366,8 @@ def set_fadesheet_flavor(node_tree, switch_on): uvmap_n.outputs['UV'], base_tex_n.inputs[0], over_tex_n.inputs[0], - base_over_mix_n.inputs['Fac'], - base_over_amix_n.inputs['Fac']) + base_over_mix_n.inputs['Factor'], + base_over_amix_n.inputs['Factor']) else: fadesheet.delete(node_tree) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_anim/anim_blend_factor_ng.py b/addon/io_scs_tools/internals/shaders/eut2/dif_anim/anim_blend_factor_ng.py index 8f02e4fc..1d7e83b6 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_anim/anim_blend_factor_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_anim/anim_blend_factor_ng.py @@ -69,16 +69,19 @@ def __create_node_group__(): blend_fac_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=BLEND_FACTOR_G) # inputs defining - blend_fac_g.inputs.new("NodeSocketFloat", "Speed") + blend_fac_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Speed") + + # outputs defining + blend_fac_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Factor") + + + # node creation input_n = blend_fac_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, start_pos_y) - # outputs defining - blend_fac_g.outputs.new("NodeSocketColor", "Factor") output_n = blend_fac_g.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 9, start_pos_y) - # node creation anim_time_n = blend_fac_g.nodes.new("ShaderNodeValue") anim_time_n.name = anim_time_n.label = _ANIM_TIME_NODE anim_time_n.location = (start_pos_x, start_pos_y + 200) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_anim/over_nmap.py b/addon/io_scs_tools/internals/shaders/eut2/dif_anim/over_nmap.py new file mode 100644 index 00000000..659877c2 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_anim/over_nmap.py @@ -0,0 +1,168 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +import bpy +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.utils import material as _material_utils +from io_scs_tools.internals.shaders.flavors import nmap + +OVER_NMAP_UVMAP_NODE = "OverNMapUVs" +OVER_NMAP_TEX_NODE = "OverNMapTex" +OVER_NMAP_COL_MIX_NODE = "OverNMapColMix" + + +def __create_nodes__(node_tree, location, factor_from): + """Create node for over normal maps. + + :param node_tree: node tree on which normal map will be used + :type node_tree: bpy.types.NodeTree + """ + + frame = node_tree.nodes[nmap.NMAP_FLAVOR_FRAME_NODE] + nmap_uvs_n = node_tree.nodes[nmap.NMAP_UVMAP_NODE] + nmap_tex_n = node_tree.nodes[nmap.NMAP_TEX_NODE] + nmap_dds16_n = node_tree.nodes[nmap.NMAP_DDS16_GNODE] + nmap_scale_n = node_tree.nodes[nmap.NMAP_SCALE_GNODE] + + # move existing + nmap_uvs_n.location.x -= 185 + nmap_tex_n.location.x -= 185 + + # nodes creation + over_nmap_uvs_n = node_tree.nodes.new("ShaderNodeUVMap") + over_nmap_uvs_n.parent = frame + over_nmap_uvs_n.name = over_nmap_uvs_n.label = OVER_NMAP_UVMAP_NODE + over_nmap_uvs_n.location = (location[0] - 185 * 5, location[1] - 400) + over_nmap_uvs_n.uv_map = _MESH_consts.none_uv + + over_nmap_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_nmap_tex_n.parent = frame + over_nmap_tex_n.name = over_nmap_tex_n.label = OVER_NMAP_TEX_NODE + over_nmap_tex_n.location = (location[0] - 185 * 4, location[1] - 400) + over_nmap_tex_n.width = 140 + + over_nmap_col_mix_n = node_tree.nodes.new("ShaderNodeMix") + over_nmap_col_mix_n.parent = frame + over_nmap_col_mix_n.name = over_nmap_col_mix_n.label = OVER_NMAP_COL_MIX_NODE + over_nmap_col_mix_n.location = (location[0] - 185 * 3, location[1] - 110) + over_nmap_col_mix_n.data_type = "RGBA" + over_nmap_col_mix_n.blend_type = "MIX" + + # links creation + node_tree.links.new(factor_from, over_nmap_col_mix_n.inputs["Factor"]) + + node_tree.links.new(over_nmap_uvs_n.outputs["UV"], over_nmap_tex_n.inputs["Vector"]) + + node_tree.links.new(nmap_tex_n.outputs["Color"], over_nmap_col_mix_n.inputs["A"]) + node_tree.links.new(over_nmap_tex_n.outputs["Color"], over_nmap_col_mix_n.inputs["B"]) + + node_tree.links.new(over_nmap_col_mix_n.outputs["Result"], nmap_dds16_n.inputs["Color"]) + node_tree.links.new(over_nmap_col_mix_n.outputs["Result"], nmap_scale_n.inputs["NMap Tex Color"]) + + +def init(node_tree, location, normal_to, normal_from, factor_from): + """Initialize normal map nodes. + + :param node_tree: node tree on which normal map will be used + :type node_tree: bpy.types.NodeTree + :param location: x position in node tree + :type location: tuple[int, int] + :param normal_to: node socket to which result of normal map material should be send + :type normal_to: bpy.types.NodeSocket + :param normal_from: node socket from which original mesh normal should be taken + :type normal_from: bpy.types.NodeSocket + :param factor_from: node socket from which factor for over blend should be taken + :type factor_from: bpy.types.NodeSocket + """ + + if nmap.NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: + nmap.init(node_tree, location, normal_to, normal_from) + __create_nodes__(node_tree, location, factor_from) + + +def set_over_texture(node_tree, image): + """Set over texture to normal map flavor. + + :param node_tree: node tree on which normal map is used + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to nmap texture node + :type image: bpy.types.Texture + """ + + # save currently active node to properly reset it on the end + # without reset of active node this material is marked as active which we don't want + old_active = node_tree.nodes.active + + # ignore empty texture + if image is None: + delete(node_tree, True) + return + + # create material node if not yet created + if nmap.NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: + return + + # assign texture to texture node first + node_tree.nodes[OVER_NMAP_TEX_NODE].image = image + + node_tree.nodes.active = old_active + + +def set_over_texture_settings(node_tree, settings): + """Set over normal map texture settings to flavor. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[OVER_NMAP_TEX_NODE], settings) + +def set_over_uv(node_tree, uv_layer): + """Set UV layer to texture in normal map flavor. + + :param node_tree: node tree on which normal map is used + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + # set uv layer to texture node + node_tree.nodes[OVER_NMAP_UVMAP_NODE].uv_map = uv_layer + + +def delete(node_tree, preserve_node=False): + """Delete over normal map nodes from node tree. + + :param node_tree: node tree from which normal map should be deleted + :type node_tree: bpy.types.NodeTree + :param preserve_node: if true node won't be deleted + :type preserve_node: bool + """ + + if OVER_NMAP_TEX_NODE in node_tree.nodes and not preserve_node: + node_tree.nodes.remove(node_tree.nodes[OVER_NMAP_UVMAP_NODE]) + node_tree.nodes.remove(node_tree.nodes[OVER_NMAP_TEX_NODE]) + node_tree.nodes.remove(node_tree.nodes[OVER_NMAP_COL_MIX_NODE]) + + nmap.delete(node_tree, preserve_node=preserve_node) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py index 23732c3a..22b539fe 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_lum/__init__.py @@ -66,19 +66,20 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] + lum_out_shader_n = node_tree.nodes[StdLum.LUM_OUT_SHADER_NODE] # set proper blend method if alpha_test.is_set(node_tree): - material.blend_method = "CLIP" - material.alpha_threshold = 0.05 + compose_lighting_n.inputs["Alpha Type"].default_value = 0.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 0.0 # add alpha test pass if multiply blend enabled, where alphed pixels shouldn't be multiplied as they are discarded if blend_mult.is_set(node_tree): - lum_out_shader_n = node_tree.nodes[StdLum.LUM_OUT_SHADER_NODE] # alpha test pass has to get fully opaque input, thus remove transparency linkage - compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] if compose_lighting_n.inputs['Alpha'].links: node_tree.links.remove(compose_lighting_n.inputs['Alpha'].links[0]) if lum_out_shader_n.inputs['Alpha'].links: @@ -91,13 +92,19 @@ def finalize(node_tree, material): alpha_test.add_pass(node_tree, shader_from, alpha_from, shader_to) if blend_add.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_mult.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_over.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" - if material.blend_method == "OPAQUE" and node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: + if compose_lighting_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links[0]) @staticmethod diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py index e1ec8042..ac5af09c 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_lum_spec/__init__.py @@ -66,19 +66,20 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] + lum_out_shader_n = node_tree.nodes[StdLum.LUM_OUT_SHADER_NODE] # set proper blend method if alpha_test.is_set(node_tree): - material.blend_method = "CLIP" - material.alpha_threshold = 0.05 + compose_lighting_n.inputs["Alpha Type"].default_value = 0.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 0.0 # add alpha test pass if multiply blend enabled, where alphed pixels shouldn't be multiplied as they are discarded if blend_mult.is_set(node_tree): - lum_out_shader_n = node_tree.nodes[StdLum.LUM_OUT_SHADER_NODE] # alpha test pass has to get fully opaque input, thus remove transparency linkage - compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] if compose_lighting_n.inputs['Alpha'].links: node_tree.links.remove(compose_lighting_n.inputs['Alpha'].links[0]) if lum_out_shader_n.inputs['Alpha'].links: @@ -91,13 +92,19 @@ def finalize(node_tree, material): alpha_test.add_pass(node_tree, shader_from, alpha_from, shader_to) if blend_add.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_mult.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_over.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + lum_out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" - if material.blend_method == "OPAQUE" and node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: + if compose_lighting_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links[0]) @staticmethod diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env_over_dif_opac/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env_over_dif_opac/__init__.py new file mode 100644 index 00000000..4878e971 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_add_env_over_dif_opac/__init__.py @@ -0,0 +1,197 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.dif_spec_add_env import DifSpecAddEnv +from io_scs_tools.internals.shaders.flavors import tg1 +from io_scs_tools.utils import material as _material_utils + + +class DifSpecAddEnvOverDifOpac(DifSpecAddEnv): + SEC_UVMAP_NODE = "SecUVMap" + OVER_TEX_NODE = "OverTex" + COLOR_MIX_NODE = "ColorMix" + ALPHA_MIX_NODE = "AlphaMix" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parents + DifSpecAddEnv.init(node_tree) + + base_tex_n = node_tree.nodes[DifSpecAddEnv.BASE_TEX_NODE] + vcol_mult_n = node_tree.nodes[DifSpecAddEnv.VCOLOR_MULT_NODE] + spec_mult_n = node_tree.nodes[DifSpecAddEnv.SPEC_MULT_NODE] + add_env_group_n = node_tree.nodes[DifSpecAddEnv.ADD_ENV_GROUP_NODE] + remap_alpha_n = node_tree.nodes[DifSpecAddEnv.REMAP_ALPHA_GNODE] + opacity_n = node_tree.nodes[DifSpecAddEnv.OPACITY_NODE] + + # Default is 1.0. I changed it to higher to make it more accurate to the game. If models preview will be broken, remove it. + add_env_n = node_tree.nodes[DifSpecAddEnv.ADD_ENV_GROUP_NODE] + add_env_n.inputs['Strength Multiplier'].default_value = 2.0 + + # move existing + spec_mult_n.location.x += pos_x_shift + add_env_group_n.location.x += pos_x_shift + opacity_n.location.y -= 300 + + # node creation + sec_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + sec_uv_n.name = sec_uv_n.label = DifSpecAddEnvOverDifOpac.SEC_UVMAP_NODE + sec_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) + sec_uv_n.uv_map = _MESH_consts.none_uv + + over_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_tex_n.name = over_tex_n.label = DifSpecAddEnvOverDifOpac.OVER_TEX_NODE + over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) + over_tex_n.width = 140 + + color_mix_n = node_tree.nodes.new("ShaderNodeMix") + color_mix_n.name = color_mix_n.label = DifSpecAddEnvOverDifOpac.COLOR_MIX_NODE + color_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1300) + color_mix_n.data_type = "RGBA" + color_mix_n.blend_type = "MIX" + + alpha_mix_n = node_tree.nodes.new("ShaderNodeMix") + alpha_mix_n.name = alpha_mix_n.label = DifSpecAddEnvOverDifOpac.ALPHA_MIX_NODE + alpha_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2000) + alpha_mix_n.data_type = "RGBA" + alpha_mix_n.blend_type = "MIX" + alpha_mix_n.inputs['B'].default_value = (0.0,) * 3 + (1.0,) + + # links creation + node_tree.links.new(sec_uv_n.outputs['UV'], over_tex_n.inputs['Vector']) + + node_tree.links.new(base_tex_n.outputs['Color'], color_mix_n.inputs['A']) + node_tree.links.new(over_tex_n.outputs['Color'], color_mix_n.inputs['B']) + node_tree.links.new(over_tex_n.outputs['Alpha'], color_mix_n.inputs['Factor']) + node_tree.links.new(over_tex_n.outputs['Alpha'], alpha_mix_n.inputs['Factor']) + + node_tree.links.new(remap_alpha_n.outputs['Weighted Alpha'], alpha_mix_n.inputs['A']) + + node_tree.links.new(alpha_mix_n.outputs['Result'], add_env_group_n.inputs['Base Texture Alpha']) + node_tree.links.new(color_mix_n.outputs['Result'], vcol_mult_n.inputs[1]) + + + @staticmethod + def set_reflection2(node_tree, value): + """Set reflection2 factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param value: reflection factor + :type value: float + """ + + pass # NOTE: reflection attribute doesn't change anything in rendered material, so pass it + + @staticmethod + def set_over_texture(node_tree, image): + """Set over texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to over texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecAddEnvOverDifOpac.OVER_TEX_NODE].image = image + + @staticmethod + def set_over_texture_settings(node_tree, settings): + """Set over texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecAddEnvOverDifOpac.OVER_TEX_NODE], settings) + + @staticmethod + def set_over_uv(node_tree, uv_layer): + """Set UV layer to over texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecAddEnvOverDifOpac.SEC_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_tg1_flavor(node_tree, switch_on): + """Set second texture generation flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + if switch_on and not tg1.is_set(node_tree): + + out_node = node_tree.nodes[DifSpecAddEnv.GEOM_NODE] + in_node = node_tree.nodes[DifSpecAddEnvOverDifOpac.OVER_TEX_NODE] + + out_node.location.x -= 185 + location = (out_node.location.x + 185, out_node.location.y) + + tg1.init(node_tree, location, out_node.outputs["Position"], in_node.inputs["Vector"]) + + elif not switch_on: + + tg1.delete(node_tree) + + @staticmethod + def set_aux1(node_tree, aux_property): + """Set second texture generation scale and rotation. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: secondary specular color represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + if tg1.is_set(node_tree): + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec/__init__.py new file mode 100644 index 00000000..a0cbcad1 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec/__init__.py @@ -0,0 +1,216 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2019: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.dif_spec import DifSpec +from io_scs_tools.internals.shaders.eut2.dif_spec_amod_dif_spec import decal_blend_factor_ng +from io_scs_tools.utils import material as _material_utils + +class DifSpecAmodDifSpec(DifSpec): + SEC_UVMAP_NODE = "SecondUVMap" + MASK_TEX_NODE = "MaskTex" + OVER_TEX_NODE = "OverTex" + DECAL_BLEND_FACTOR_NODE = "DecalBlendFactorGNode" + MASK_VCOLOR_MIX_NODE = "MaskVertexColorMix" + MASK_COLOR_MIX_NODE = "MaskColorMix" + + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + DifSpec.init(node_tree, disable_remap_alpha=False) + + base_tex_n = node_tree.nodes[DifSpec.BASE_TEX_NODE] + vcol_multi_n = node_tree.nodes[DifSpec.VCOLOR_MULT_NODE] + vcol_group_n = node_tree.nodes[DifSpec.VCOL_GROUP_NODE] + + # delete existing + node_tree.nodes.remove(node_tree.nodes[DifSpec.OPACITY_NODE]) + + # node creation + # - column -1 - + sec_uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") + sec_uvmap_n.name = DifSpecAmodDifSpec.SEC_UVMAP_NODE + sec_uvmap_n.label = DifSpecAmodDifSpec.SEC_UVMAP_NODE + sec_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) + sec_uvmap_n.uv_map = _MESH_consts.none_uv + + # - column 1 - + deacl_blend_fac_gn = node_tree.nodes.new("ShaderNodeGroup") + deacl_blend_fac_gn.name = DifSpecAmodDifSpec.DECAL_BLEND_FACTOR_NODE + deacl_blend_fac_gn.label = DifSpecAmodDifSpec.DECAL_BLEND_FACTOR_NODE + deacl_blend_fac_gn.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) + deacl_blend_fac_gn.node_tree = decal_blend_factor_ng.get_node_group() + + mask_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_tex_n.name = DifSpecAmodDifSpec.MASK_TEX_NODE + mask_tex_n.label = DifSpecAmodDifSpec.MASK_TEX_NODE + mask_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1000) + mask_tex_n.width = 140 + + over_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_tex_n.name = DifSpecAmodDifSpec.OVER_TEX_NODE + over_tex_n.label = DifSpecAmodDifSpec.OVER_TEX_NODE + over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 700) + over_tex_n.width = 140 + + # - column 2 - + mask_vcol_mix_n = node_tree.nodes.new("ShaderNodeMix") + mask_vcol_mix_n.name = DifSpecAmodDifSpec.MASK_VCOLOR_MIX_NODE + mask_vcol_mix_n.label = DifSpecAmodDifSpec.MASK_VCOLOR_MIX_NODE + mask_vcol_mix_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 1200) + mask_vcol_mix_n.data_type = "RGBA" + mask_vcol_mix_n.blend_type = "MIX" + mask_vcol_mix_n.inputs['A'].default_value = (0,) * 3 + (1,) + + # - column 3 - + mask_color_mix_n = node_tree.nodes.new("ShaderNodeMix") + mask_color_mix_n.name = DifSpecAmodDifSpec.MASK_COLOR_MIX_NODE + mask_color_mix_n.label = DifSpecAmodDifSpec.MASK_COLOR_MIX_NODE + mask_color_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1200) + mask_color_mix_n.data_type = "RGBA" + mask_color_mix_n.blend_type = "MIX" + + + + # links creation + # - Column -1 - + node_tree.links.new(vcol_group_n.outputs['Vertex Color Alpha'], deacl_blend_fac_gn.inputs['Vertex Alpha']) + node_tree.links.new(sec_uvmap_n.outputs['UV'], mask_tex_n.inputs['Vector']) + node_tree.links.new(sec_uvmap_n.outputs['UV'], over_tex_n.inputs['Vector']) + + # - Column 1 - + node_tree.links.new(base_tex_n.outputs['Color'], mask_color_mix_n.inputs['A']) + node_tree.links.new(mask_tex_n.outputs['Color'], mask_vcol_mix_n.inputs['B']) + node_tree.links.new(over_tex_n.outputs['Color'], mask_color_mix_n.inputs['B']) + node_tree.links.new(deacl_blend_fac_gn.outputs['Factor'], mask_vcol_mix_n.inputs['Factor']) + + # - Column 2 - + node_tree.links.new(mask_vcol_mix_n.outputs['Result'], mask_color_mix_n.inputs['Factor']) + + # - Column 3 - + node_tree.links.new(mask_color_mix_n.outputs['Result'], vcol_multi_n.inputs[1]) + + + @staticmethod + def set_aux0(node_tree, aux_property): + """Set decal blending factors to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: decal blending factors represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + node_tree.nodes[DifSpecAmodDifSpec.DECAL_BLEND_FACTOR_NODE].inputs["Factor1"].default_value = aux_property[0]['value'] + node_tree.nodes[DifSpecAmodDifSpec.DECAL_BLEND_FACTOR_NODE].inputs["Factor2"].default_value = aux_property[1]['value'] + + @staticmethod + def set_mask_uv(node_tree, uv_layer): + """Set UV layer to mask texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mask texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecAmodDifSpec.SEC_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_mask_texture(node_tree, image): + """Set mask texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mask texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecAmodDifSpec.MASK_TEX_NODE].image = image + + @staticmethod + def set_mask_texture_settings(node_tree, settings): + """Set mask texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecAmodDifSpec.MASK_TEX_NODE], settings) + + # due the fact uvs get clamped in vertex shader, we have to manually switch repeat on, for effect to work correctly + node_tree.nodes[DifSpecAmodDifSpec.MASK_TEX_NODE].extension = "REPEAT" + + @staticmethod + def set_over_uv(node_tree, uv_layer): + """Set UV layer to overlying texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over texture + :type uv_layer: str + """ + + DifSpecAmodDifSpec.set_mask_uv(node_tree, uv_layer) + + @staticmethod + def set_over_texture(node_tree, image): + """Set over texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mult texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecAmodDifSpec.OVER_TEX_NODE].image = image + + @staticmethod + def set_over_texture_settings(node_tree, settings): + """Set mask texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecAmodDifSpec.OVER_TEX_NODE], settings) + \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec/decal_blend_factor_ng.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec/decal_blend_factor_ng.py new file mode 100644 index 00000000..f39e74ad --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec/decal_blend_factor_ng.py @@ -0,0 +1,156 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015: SCS Software + +from multiprocessing.spawn import import_main_path +import bpy +from io_scs_tools.consts import Material as _MAT_consts + +DECAL_BLEND_FACTOR_G = _MAT_consts.node_group_prefix + "DecalBlendFactor" + +_MAP_VCOLA_SHIFT_NODE = "MapVColAlphaShift" +_INVERT_NEG_NODE = "InvertNegatives" +_ALPHA_1_SCALE_NODE = "AlphaScale_1" +_ALPHA_2_SCALE_NODE = "AlphaScale_2" +_BLEND_1_ABS_NODE = "BlendingAbsolute_1" +_BLEND_2_ABS_NODE = "BlendingAbsolute_2" +_MULT_1_SCALE_NODE = "MultScale_1" +_MULT_2_SCALE_NODE = "MultScale_2" +_MULT_FINAL_NODE = "MultFinal" + +def get_node_group(): + """Gets node group for combining of Decal blending factors with mask texture. + + :return: node group which calculates change factor + :rtype: bpy.types.NodeGroup + """ + + if DECAL_BLEND_FACTOR_G not in bpy.data.node_groups: + __create_node_group__() + + return bpy.data.node_groups[DECAL_BLEND_FACTOR_G] + +def __create_node_group__(): + """Creates decal blending factor group. + + Inputs: Decal blending factor (1 & 2) and Vertex Color Alpha + Outputs: Factor + """ + + pos_x_shift = 185 + + dec_blend_fac_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=DECAL_BLEND_FACTOR_G) + + # inputs defining + dec_blend_fac_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Vertex Alpha") + dec_blend_fac_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Factor1") + dec_blend_fac_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Factor2") + + # outputs defining + dec_blend_fac_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Factor") + + + # nodes creation + input_n = dec_blend_fac_g.nodes.new("NodeGroupInput") + input_n.location = (0, 0) + + output_n = dec_blend_fac_g.nodes.new("NodeGroupOutput") + output_n.location = (pos_x_shift * 6, 0) + + # - column 1 - + map_vcol_a_shift_n = dec_blend_fac_g.nodes.new("ShaderNodeMapping") + map_vcol_a_shift_n.name = map_vcol_a_shift_n.label = _MAP_VCOLA_SHIFT_NODE + map_vcol_a_shift_n.location = (pos_x_shift, 100) + map_vcol_a_shift_n.vector_type = "POINT" + map_vcol_a_shift_n.inputs['Location'].default_value = (-0.5,) * 3 + + # - column 2 - + invert_neg_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + invert_neg_n.name = invert_neg_n.label = _INVERT_NEG_NODE + invert_neg_n.location = (pos_x_shift * 2, 250) + invert_neg_n.operation = "MULTIPLY" + invert_neg_n.inputs[1].default_value = (-1,) * 3 + + # - column 3 - + alpha_1_scale_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + alpha_1_scale_n.name = alpha_1_scale_n.label = _ALPHA_1_SCALE_NODE + alpha_1_scale_n.location = (pos_x_shift * 3, 250) + alpha_1_scale_n.operation = "MULTIPLY" + alpha_1_scale_n.inputs[1].default_value = (2,) * 3 + + blending_1_abs_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + blending_1_abs_n.name = blending_1_abs_n.label = _BLEND_1_ABS_NODE + blending_1_abs_n.location = (pos_x_shift * 3, 50) + blending_1_abs_n.operation = "ABSOLUTE" + + alpha_2_scale_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + alpha_2_scale_n.name = alpha_2_scale_n.label = _ALPHA_2_SCALE_NODE + alpha_2_scale_n.location = (pos_x_shift * 3, -100) + alpha_2_scale_n.operation = "MULTIPLY" + alpha_2_scale_n.inputs[1].default_value = (2,) * 3 + + blending_2_abs_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + blending_2_abs_n.name = blending_2_abs_n.label = _BLEND_2_ABS_NODE + blending_2_abs_n.location = (pos_x_shift * 3, -300) + blending_2_abs_n.operation = "ABSOLUTE" + + # - column 4 - + mult_1_scale_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + mult_1_scale_n.name = mult_1_scale_n.label = _MULT_1_SCALE_NODE + mult_1_scale_n.location = (pos_x_shift * 4, 100) + mult_1_scale_n.operation = "MULTIPLY" + + mult_2_scale_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + mult_2_scale_n.name = mult_2_scale_n.label = _MULT_2_SCALE_NODE + mult_2_scale_n.location = (pos_x_shift * 4, -200) + mult_2_scale_n.operation = "MULTIPLY" + + # - column 5 - + mult_final_n = dec_blend_fac_g.nodes.new("ShaderNodeVectorMath") + mult_final_n.name = mult_final_n.label = _MULT_FINAL_NODE + mult_final_n.location = (pos_x_shift * 5, 0) + mult_final_n.operation = "MAXIMUM" + + + # links creation + # - column 0 - + dec_blend_fac_g.links.new(input_n.outputs["Vertex Alpha"], map_vcol_a_shift_n.inputs["Vector"]) + dec_blend_fac_g.links.new(input_n.outputs["Factor1"], blending_1_abs_n.inputs["Vector"]) + dec_blend_fac_g.links.new(input_n.outputs["Factor2"], blending_2_abs_n.inputs["Vector"]) + + # - column 1 - + dec_blend_fac_g.links.new(map_vcol_a_shift_n.outputs["Vector"], invert_neg_n.inputs[0]) + dec_blend_fac_g.links.new(map_vcol_a_shift_n.outputs["Vector"], alpha_2_scale_n.inputs[0]) + + # - column 2 - + dec_blend_fac_g.links.new(invert_neg_n.outputs["Vector"], alpha_1_scale_n.inputs[0]) + + # - column 3 - + dec_blend_fac_g.links.new(alpha_1_scale_n.outputs["Vector"], mult_1_scale_n.inputs[0]) + dec_blend_fac_g.links.new(blending_1_abs_n.outputs["Vector"], mult_1_scale_n.inputs[1]) + + dec_blend_fac_g.links.new(alpha_2_scale_n.outputs["Vector"], mult_2_scale_n.inputs[0]) + dec_blend_fac_g.links.new(blending_2_abs_n.outputs["Vector"], mult_2_scale_n.inputs[1]) + + # - column 4 - + dec_blend_fac_g.links.new(mult_1_scale_n.outputs["Vector"], mult_final_n.inputs[0]) + dec_blend_fac_g.links.new(mult_2_scale_n.outputs["Vector"], mult_final_n.inputs[1]) + + # - column 5 - + dec_blend_fac_g.links.new(mult_final_n.outputs["Vector"], output_n.inputs["Factor"]) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec_add_env/__init__.py new file mode 100644 index 00000000..07fabf76 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_amod_dif_spec_add_env/__init__.py @@ -0,0 +1,47 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.internals.shaders.eut2.dif_spec_amod_dif_spec import DifSpecAmodDifSpec +from io_scs_tools.internals.shaders.eut2.std_passes.add_env import StdAddEnv + + +class DifSpecAmodDifSpecAddEnv(DifSpecAmodDifSpec, StdAddEnv): + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + # init parents + DifSpecAmodDifSpec.init(node_tree) + StdAddEnv.add(node_tree, + DifSpecAmodDifSpec.GEOM_NODE, + node_tree.nodes[DifSpecAmodDifSpec.SPEC_COL_NODE].outputs['Color'], + node_tree.nodes[DifSpecAmodDifSpec.REMAP_ALPHA_GNODE].outputs['Weighted Alpha'], + node_tree.nodes[DifSpecAmodDifSpec.LIGHTING_EVAL_NODE].outputs['Normal'], + node_tree.nodes[DifSpecAmodDifSpec.COMPOSE_LIGHTING_NODE].inputs['Env Color']) + diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/__init__.py index 11e2592b..960a196f 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/__init__.py @@ -66,7 +66,7 @@ def init(node_tree): # node creation uv_scale_n = node_tree.nodes.new("ShaderNodeValue") uv_scale_n.name = uv_scale_n.label = DifSpecFadeDifSpec.UV_SCALE_NODE - uv_scale_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1200) + uv_scale_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1100) detail_uv_scaling_n = node_tree.nodes.new("ShaderNodeVectorMath") detail_uv_scaling_n.name = detail_uv_scaling_n.label = DifSpecFadeDifSpec.DETAIL_UV_SCALING_NODE @@ -83,14 +83,16 @@ def init(node_tree): detail_setup_group_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) detail_setup_group_n.node_tree = detail_setup_ng.get_node_group() - base_detail_mix_a_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_detail_mix_a_n = node_tree.nodes.new("ShaderNodeMix") base_detail_mix_a_n.name = base_detail_mix_a_n.label = DifSpecFadeDifSpec.BASE_DETAIL_MIX_A_NODE base_detail_mix_a_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1700) + base_detail_mix_a_n.data_type = "RGBA" base_detail_mix_a_n.blend_type = "MIX" - base_detail_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_detail_mix_n = node_tree.nodes.new("ShaderNodeMix") base_detail_mix_n.name = base_detail_mix_n.label = DifSpecFadeDifSpec.BASE_DETAIL_MIX_NODE base_detail_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1400) + base_detail_mix_n.data_type = "RGBA" base_detail_mix_n.blend_type = "MIX" # links creation @@ -101,19 +103,19 @@ def init(node_tree): node_tree.links.new(detail_tex_n.inputs['Vector'], detail_uv_scaling_n.outputs[0]) # pass 1 - node_tree.links.new(base_detail_mix_a_n.inputs['Fac'], detail_setup_group_n.outputs['Blend Factor']) - node_tree.links.new(base_detail_mix_a_n.inputs['Color1'], base_tex_n.outputs['Alpha']) - node_tree.links.new(base_detail_mix_a_n.inputs['Color2'], detail_tex_n.outputs['Alpha']) + node_tree.links.new(base_detail_mix_a_n.inputs['Factor'], detail_setup_group_n.outputs['Blend Factor']) + node_tree.links.new(base_detail_mix_a_n.inputs['A'], base_tex_n.outputs['Alpha']) + node_tree.links.new(base_detail_mix_a_n.inputs['B'], detail_tex_n.outputs['Alpha']) - node_tree.links.new(base_detail_mix_n.inputs['Fac'], detail_setup_group_n.outputs['Blend Factor']) - node_tree.links.new(base_detail_mix_n.inputs['Color1'], base_tex_n.outputs['Color']) - node_tree.links.new(base_detail_mix_n.inputs['Color2'], detail_tex_n.outputs['Color']) + node_tree.links.new(base_detail_mix_n.inputs['Factor'], detail_setup_group_n.outputs['Blend Factor']) + node_tree.links.new(base_detail_mix_n.inputs['A'], base_tex_n.outputs['Color']) + node_tree.links.new(base_detail_mix_n.inputs['B'], detail_tex_n.outputs['Color']) # pass 2 - node_tree.links.new(spec_mult_n.inputs[1], base_detail_mix_a_n.outputs['Color']) + node_tree.links.new(spec_mult_n.inputs[1], base_detail_mix_a_n.outputs['Result']) # pass 3 - node_tree.links.new(vcol_mult_n.inputs[1], base_detail_mix_n.outputs['Color']) + node_tree.links.new(vcol_mult_n.inputs[1], base_detail_mix_n.outputs['Result']) @staticmethod def set_detail_texture(node_tree, image): diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_setup_ng.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_setup_ng.py index 4c199d30..a0f6df30 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_setup_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_fade_dif_spec/detail_setup_ng.py @@ -55,19 +55,22 @@ def __create_group__(): detail_setup_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=DETAIL_SETUP_G) # inputs defining - detail_setup_g.inputs.new("NodeSocketFloat", "Fade From") - detail_setup_g.inputs.new("NodeSocketFloat", "Fade Range") - detail_setup_g.inputs.new("NodeSocketFloat", "Blend Bias") + detail_setup_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Fade From") + detail_setup_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Fade Range") + detail_setup_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Blend Bias") + + # outputs defining + detail_setup_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Detail Strength") + detail_setup_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Blend Factor") + + + # group nodes input_n = detail_setup_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x, start_pos_y) - # outputs defining - detail_setup_g.outputs.new("NodeSocketFloat", "Detail Strength") - detail_setup_g.outputs.new("NodeSocketFloat", "Blend Factor") output_n = detail_setup_g.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y) - # group nodes camera_data_n = detail_setup_g.nodes.new("ShaderNodeCameraData") camera_data_n.location = (start_pos_x, start_pos_y + 100) @@ -109,14 +112,15 @@ def __create_group__(): detail_setup_g.links.new(output_n.inputs['Detail Strength'], equation_nodes[i].outputs[0]) - blend_factor_mix_n = detail_setup_g.nodes.new("ShaderNodeMixRGB") + blend_factor_mix_n = detail_setup_g.nodes.new("ShaderNodeMix") blend_factor_mix_n.name = blend_factor_mix_n.label = "BlendFactor" blend_factor_mix_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y - 50) + blend_factor_mix_n.data_type = "RGBA" blend_factor_mix_n.blend_type = "MIX" - blend_factor_mix_n.inputs['Color2'].default_value = (0.0,) * 4 + blend_factor_mix_n.inputs['B'].default_value = (0.0,) * 4 # group links - detail_setup_g.links.new(blend_factor_mix_n.inputs['Fac'], input_n.outputs['Blend Bias']) - detail_setup_g.links.new(blend_factor_mix_n.inputs['Color1'], equation_nodes[-1].outputs[0]) + detail_setup_g.links.new(blend_factor_mix_n.inputs['Factor'], input_n.outputs['Blend Bias']) + detail_setup_g.links.new(blend_factor_mix_n.inputs['A'], equation_nodes[-1].outputs[0]) - detail_setup_g.links.new(output_n.inputs['Blend Factor'], blend_factor_mix_n.outputs['Color']) + detail_setup_g.links.new(output_n.inputs['Blend Factor'], blend_factor_mix_n.outputs['Result']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_iamod_dif_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_iamod_dif_add_env/__init__.py new file mode 100644 index 00000000..1ea395e5 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_iamod_dif_add_env/__init__.py @@ -0,0 +1,220 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2019: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.std_node_groups import alpha_remap_ng +from io_scs_tools.internals.shaders.eut2.dif_spec_mult_dif_spec import DifSpecMultDifSpec +from io_scs_tools.internals.shaders.eut2.std_passes.add_env import StdAddEnv +from io_scs_tools.utils import material as _material_utils + +class DifSpecMultDifIamodDifAddEnv(DifSpecMultDifSpec, StdAddEnv): + THIRD_UVMAP_NODE = "ThirdUVMap" + IAMOD_TEX_NODE = "IamodTex" + IAMOD_SCALE_NODE = "IamodScaled" + IAMOD_MULTBASE_COL_MIX_NODE = "IamodMultBaseColorMix" + REMAP_ALPHA_GNODE = "RemapAlphaToWeight" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + DifSpecMultDifSpec.init(node_tree) + + # node creation + remap_alpha_n = node_tree.nodes.new("ShaderNodeGroup") + remap_alpha_n.name = remap_alpha_n.label = DifSpecMultDifSpec.REMAP_ALPHA_GNODE + remap_alpha_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 1800) + remap_alpha_n.node_tree = alpha_remap_ng.get_node_group() + remap_alpha_n.inputs['Factor1'].default_value = 1.0 + remap_alpha_n.inputs['Factor2'].default_value = 0.0 + + StdAddEnv.add(node_tree, + DifSpecMultDifSpec.GEOM_NODE, + node_tree.nodes[DifSpecMultDifSpec.SPEC_COL_NODE].outputs['Color'], + node_tree.nodes[DifSpecMultDifSpec.REMAP_ALPHA_GNODE].outputs['Weighted Alpha'], + node_tree.nodes[DifSpecMultDifSpec.LIGHTING_EVAL_NODE].outputs['Normal'], + node_tree.nodes[DifSpecMultDifSpec.COMPOSE_LIGHTING_NODE].inputs['Env Color']) + + base_tex_n = node_tree.nodes[DifSpecMultDifSpec.BASE_TEX_NODE] + mult_tex_n = node_tree.nodes[DifSpecMultDifSpec.MULT_TEX_NODE] + add_env_gn = node_tree.nodes[StdAddEnv.ADD_ENV_GROUP_NODE] + + vcol_group_n = node_tree.nodes[DifSpecMultDifSpec.VCOL_GROUP_NODE] + mult_base_col_mix_n = node_tree.nodes[DifSpecMultDifSpec.MULT_BASE_COL_MIX_NODE] + vcol_scale_n = node_tree.nodes[DifSpecMultDifSpec.VCOLOR_SCALE_NODE] + vcol_mult_n = node_tree.nodes[DifSpecMultDifSpec.VCOLOR_MULT_NODE] + diff_mult_n = node_tree.nodes[DifSpecMultDifSpec.DIFF_MULT_NODE] + spec_mult_n = node_tree.nodes[DifSpecMultDifSpec.SPEC_MULT_NODE] + + # delete existing + node_tree.nodes.remove(node_tree.nodes[DifSpecMultDifIamodDifAddEnv.MULT_BASE_A_MIX_NODE]) + + # move existing + spec_mult_n.location.x += pos_x_shift + spec_mult_n.location.x -= pos_x_shift * 2 + + vcol_scale_n.location.y -= 200 + vcol_mult_n.location.y -= 200 + diff_mult_n.location.y -= 200 + mult_base_col_mix_n.location.y -= 200 + + # node creation + third_uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") + third_uvmap_n.name = DifSpecMultDifIamodDifAddEnv.THIRD_UVMAP_NODE + third_uvmap_n.label = DifSpecMultDifIamodDifAddEnv.THIRD_UVMAP_NODE + third_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 750) + third_uvmap_n.uv_map = _MESH_consts.none_uv + + iamod_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + iamod_tex_n.name = DifSpecMultDifIamodDifAddEnv.IAMOD_TEX_NODE + iamod_tex_n.label = DifSpecMultDifIamodDifAddEnv.IAMOD_TEX_NODE + iamod_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) + iamod_tex_n.width = 140 + + iamod_scale_col_n = node_tree.nodes.new("ShaderNodeMix") + iamod_scale_col_n.name = DifSpecMultDifIamodDifAddEnv.IAMOD_SCALE_NODE + iamod_scale_col_n.label = DifSpecMultDifIamodDifAddEnv.IAMOD_SCALE_NODE + iamod_scale_col_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1000) + iamod_scale_col_n.data_type = "RGBA" + iamod_scale_col_n.blend_type = "MIX" + iamod_scale_col_n.inputs['B'].default_value = (1,) * 4 + + iamod_multbase_col_mix_n = node_tree.nodes.new("ShaderNodeVectorMath") + iamod_multbase_col_mix_n.name = DifSpecMultDifIamodDifAddEnv.IAMOD_MULTBASE_COL_MIX_NODE + iamod_multbase_col_mix_n.label = DifSpecMultDifIamodDifAddEnv.IAMOD_MULTBASE_COL_MIX_NODE + iamod_multbase_col_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1100) + iamod_multbase_col_mix_n.operation = "MULTIPLY" + + # links creation + node_tree.links.new(add_env_gn.inputs['Strength Multiplier'], mult_tex_n.outputs['Alpha']) + node_tree.links.new(remap_alpha_n.inputs['Alpha'], base_tex_n.outputs['Alpha']) + + node_tree.links.new(iamod_tex_n.inputs['Vector'], third_uvmap_n.outputs['UV']) + + node_tree.links.new(iamod_scale_col_n.inputs['Factor'], vcol_group_n.outputs['Vertex Color Alpha']) + node_tree.links.new(iamod_scale_col_n.inputs['A'], iamod_tex_n.outputs['Color']) + + node_tree.links.new(iamod_multbase_col_mix_n.inputs[0], mult_base_col_mix_n.outputs['Vector']) + node_tree.links.new(iamod_multbase_col_mix_n.inputs[1], iamod_scale_col_n.outputs['Result']) + + node_tree.links.new(vcol_mult_n.inputs[1], iamod_multbase_col_mix_n.outputs['Vector']) + + node_tree.links.new(spec_mult_n.inputs[1], remap_alpha_n.outputs['Weighted Alpha']) + + @staticmethod + def set_iamod_texture(node_tree, image): + """Set inverse alpha modulating texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to iamod texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecMultDifIamodDifAddEnv.IAMOD_TEX_NODE].image = image + + @staticmethod + def set_iamod_texture_settings(node_tree, settings): + """Set inverse alpha modulating texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecMultDifIamodDifAddEnv.IAMOD_TEX_NODE], settings) + + @staticmethod + def set_iamod_uv(node_tree, uv_layer): + """Set UV layer to inverse alpha modulating texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for iamod texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecMultDifIamodDifAddEnv.THIRD_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_alpha_test_flavor(node_tree, switch_on): + """Set alpha test flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if alpha test should be switched on or off + :type switch_on: bool + """ + + pass # NOTE: no support for this flavor; overriding with empty function + + @staticmethod + def set_blend_over_flavor(node_tree, switch_on): + """Set blend over flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if blend over should be switched on or off + :type switch_on: bool + """ + + pass # NOTE: no support for this flavor; overriding with empty function + + @staticmethod + def set_blend_add_flavor(node_tree, switch_on): + """Set blend add flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if blend add should be switched on or off + :type switch_on: bool + """ + + pass # NOTE: no support for this flavor; overriding with empty function + + @staticmethod + def set_blend_mult_flavor(node_tree, switch_on): + """Set blend mult flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if blend mult should be switched on or off + :type switch_on: bool + """ + + pass # NOTE: no support for this flavor; overriding with empty function diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/__init__.py index 17910065..d0ac7e5c 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec/__init__.py @@ -21,6 +21,7 @@ from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.eut2.dif_spec import DifSpec from io_scs_tools.internals.shaders.flavors import tg1 +from io_scs_tools.internals.shaders.flavors import opasrc1 from io_scs_tools.utils import material as _material_utils @@ -69,7 +70,7 @@ def init(node_tree): sec_uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") sec_uvmap_n.name = DifSpecMultDifSpec.SEC_UVMAP_NODE sec_uvmap_n.label = DifSpecMultDifSpec.SEC_UVMAP_NODE - sec_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1200) + sec_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) sec_uvmap_n.uv_map = _MESH_consts.none_uv mult_tex_n = node_tree.nodes.new("ShaderNodeTexImage") @@ -167,7 +168,7 @@ def set_tg1_flavor(node_tree, switch_on): @staticmethod def set_aux1(node_tree, aux_property): - """Set second texture generation scale. + """Set second texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -176,5 +177,34 @@ def set_aux1(node_tree, aux_property): """ if tg1.is_set(node_tree): + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) - tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + @staticmethod + def set_opasrc1_flavor(node_tree, switch_on): + """Set opasrc1 flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + vcol_group_n = node_tree.nodes[DifSpecMultDifSpec.VCOL_GROUP_NODE] + base_tex_n = node_tree.nodes[DifSpecMultDifSpec.BASE_TEX_NODE] + + compose_lighting_n = node_tree.nodes[DifSpecMultDifSpec.COMPOSE_LIGHTING_NODE] + + if switch_on: + + location = (5 * 185, 1800) + opasrc1.init(node_tree, location, + vcol_group_n.outputs["Vertex Color Alpha"], + base_tex_n.outputs["Alpha"], + compose_lighting_n.inputs["Alpha"]) + + else: + opasrc1.delete(node_tree) \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec_iamod_dif_spec/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec_iamod_dif_spec/__init__.py index 39aba7e1..49ae8338 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec_iamod_dif_spec/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_mult_dif_spec_iamod_dif_spec/__init__.py @@ -72,7 +72,7 @@ def init(node_tree): third_uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") third_uvmap_n.name = DifSpecMultDifSpecIamodDifSpec.THIRD_UVMAP_NODE third_uvmap_n.label = DifSpecMultDifSpecIamodDifSpec.THIRD_UVMAP_NODE - third_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) + third_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 750) third_uvmap_n.uv_map = _MESH_consts.none_uv iamod_tex_n = node_tree.nodes.new("ShaderNodeTexImage") @@ -81,19 +81,21 @@ def init(node_tree): iamod_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) iamod_tex_n.width = 140 - iamod_scale_col_n = node_tree.nodes.new("ShaderNodeMixRGB") + iamod_scale_col_n = node_tree.nodes.new("ShaderNodeMix") iamod_scale_col_n.name = DifSpecMultDifSpecIamodDifSpec.IAMOD_SCALE_NODE iamod_scale_col_n.label = DifSpecMultDifSpecIamodDifSpec.IAMOD_SCALE_NODE iamod_scale_col_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1000) + iamod_scale_col_n.data_type = "RGBA" iamod_scale_col_n.blend_type = "MIX" - iamod_scale_col_n.inputs['Color2'].default_value = (1,) * 4 + iamod_scale_col_n.inputs['B'].default_value = (1,) * 4 - iamod_scale_a_n = node_tree.nodes.new("ShaderNodeMixRGB") + iamod_scale_a_n = node_tree.nodes.new("ShaderNodeMix") iamod_scale_a_n.name = DifSpecMultDifSpecIamodDifSpec.IAMOD_SCALE_A_NODE iamod_scale_a_n.label = DifSpecMultDifSpecIamodDifSpec.IAMOD_SCALE_A_NODE iamod_scale_a_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1500) + iamod_scale_a_n.data_type = "RGBA" iamod_scale_a_n.blend_type = "MIX" - iamod_scale_a_n.inputs['Color2'].default_value = (1,) * 4 + iamod_scale_a_n.inputs['B'].default_value = (1,) * 4 iamod_multbase_col_mix_n = node_tree.nodes.new("ShaderNodeVectorMath") iamod_multbase_col_mix_n.name = DifSpecMultDifSpecIamodDifSpec.IAMOD_MULTBASE_COL_MIX_NODE @@ -110,17 +112,17 @@ def init(node_tree): # links creation node_tree.links.new(iamod_tex_n.inputs['Vector'], third_uvmap_n.outputs['UV']) - node_tree.links.new(iamod_scale_col_n.inputs['Fac'], vcol_group_n.outputs['Vertex Color Alpha']) - node_tree.links.new(iamod_scale_col_n.inputs['Color1'], iamod_tex_n.outputs['Color']) + node_tree.links.new(iamod_scale_col_n.inputs['Factor'], vcol_group_n.outputs['Vertex Color Alpha']) + node_tree.links.new(iamod_scale_col_n.inputs['A'], iamod_tex_n.outputs['Color']) - node_tree.links.new(iamod_scale_a_n.inputs['Fac'], vcol_group_n.outputs['Vertex Color Alpha']) - node_tree.links.new(iamod_scale_a_n.inputs['Color1'], iamod_tex_n.outputs['Alpha']) + node_tree.links.new(iamod_scale_a_n.inputs['Factor'], vcol_group_n.outputs['Vertex Color Alpha']) + node_tree.links.new(iamod_scale_a_n.inputs['A'], iamod_tex_n.outputs['Alpha']) node_tree.links.new(iamod_multbase_col_mix_n.inputs[0], mult_base_col_mix_n.outputs[0]) - node_tree.links.new(iamod_multbase_col_mix_n.inputs[1], iamod_scale_col_n.outputs['Color']) + node_tree.links.new(iamod_multbase_col_mix_n.inputs[1], iamod_scale_col_n.outputs['Result']) node_tree.links.new(iamod_multbase_a_mix_n.inputs[0], mult_base_a_mix_n.outputs['Value']) - node_tree.links.new(iamod_multbase_a_mix_n.inputs[1], iamod_scale_a_n.outputs['Color']) + node_tree.links.new(iamod_multbase_a_mix_n.inputs[1], iamod_scale_a_n.outputs['Result']) node_tree.links.new(vcol_mult_n.inputs[1], iamod_multbase_col_mix_n.outputs[0]) node_tree.links.new(spec_mult_n.inputs[1], iamod_multbase_a_mix_n.outputs['Value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu/__init__.py index be4917b6..9ebd3623 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu/__init__.py @@ -20,6 +20,7 @@ from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.eut2.dif_spec import DifSpec +from io_scs_tools.internals.shaders.flavors import tg1 from io_scs_tools.utils import material as _material_utils @@ -63,7 +64,7 @@ def init(node_tree): # node creation sec_uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") sec_uvmap_n.name = sec_uvmap_n.label = DifSpecOclu.SEC_UVMAP_NODE - sec_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1100) + sec_uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) sec_uvmap_n.uv_map = _MESH_consts.none_uv oclu_tex_n = node_tree.nodes.new("ShaderNodeTexImage") @@ -71,7 +72,7 @@ def init(node_tree): oclu_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) oclu_tex_n.width = 140 - oclu_sep_rgb_n = node_tree.nodes.new("ShaderNodeSeparateRGB") + oclu_sep_rgb_n = node_tree.nodes.new("ShaderNodeSeparateColor") oclu_sep_rgb_n.name = oclu_sep_rgb_n.label = DifSpecOclu.OCLU_SEPARATE_RGB_NODE oclu_sep_rgb_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1200) @@ -89,14 +90,14 @@ def init(node_tree): node_tree.links.new(oclu_tex_n.inputs["Vector"], sec_uvmap_n.outputs["UV"]) # pass 1 - node_tree.links.new(oclu_sep_rgb_n.inputs["Image"], oclu_tex_n.outputs["Color"]) + node_tree.links.new(oclu_sep_rgb_n.inputs["Color"], oclu_tex_n.outputs["Color"]) # pass 2 node_tree.links.new(oclu_a_mix_n.inputs[0], base_tex_n.outputs["Alpha"]) - node_tree.links.new(oclu_a_mix_n.inputs[1], oclu_sep_rgb_n.outputs["R"]) + node_tree.links.new(oclu_a_mix_n.inputs[1], oclu_sep_rgb_n.outputs["Red"]) node_tree.links.new(oclu_mix_n.inputs[0], base_tex_n.outputs["Color"]) - node_tree.links.new(oclu_mix_n.inputs[1], oclu_sep_rgb_n.outputs["R"]) + node_tree.links.new(oclu_mix_n.inputs[1], oclu_sep_rgb_n.outputs["Red"]) # pass 3 node_tree.links.new(spec_mult_n.inputs[1], oclu_a_mix_n.outputs["Value"]) @@ -141,3 +142,44 @@ def set_oclu_uv(node_tree, uv_layer): uv_layer = _MESH_consts.none_uv node_tree.nodes[DifSpecOclu.SEC_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_tg1_flavor(node_tree, switch_on): + """Set zero texture generation flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + if switch_on and not tg1.is_set(node_tree): + + out_node = node_tree.nodes[DifSpecOclu.GEOM_NODE] + in_node = node_tree.nodes[DifSpecOclu.OCLU_TEX_NODE] + + out_node.location.x -= 185 + location = (out_node.location.x + 185, out_node.location.y) + + tg1.init(node_tree, location, out_node.outputs["Position"], in_node.inputs["Vector"]) + + elif not switch_on: + + tg1.delete(node_tree) + + @staticmethod + def set_aux1(node_tree, aux_property): + """Set second texture generation scale and rotation. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: secondary specular color represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + if tg1.is_set(node_tree): + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py index 73c20811..1d785201 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_add_env/__init__.py @@ -50,4 +50,4 @@ def init(node_tree): add_env_gn = node_tree.nodes[StdAddEnv.ADD_ENV_GROUP_NODE] # links creation - node_tree.links.new(add_env_gn.inputs['Strength Multiplier'], oclu_sep_n.outputs['R']) + node_tree.links.new(add_env_gn.inputs['Strength Multiplier'], oclu_sep_n.outputs['Red']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_weight_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_weight_add_env/__init__.py index e89d4757..8d291a57 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_weight_add_env/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_oclu_weight_add_env/__init__.py @@ -71,4 +71,4 @@ def init(node_tree): # links creation node_tree.links.new(add_env_gn.inputs['Weighted Color'], vcol_scale_n.outputs[0]) - node_tree.links.new(add_env_gn.inputs['Strength Multiplier'], oclu_sep_n.outputs['R']) + node_tree.links.new(add_env_gn.inputs['Strength Multiplier'], oclu_sep_n.outputs['Red']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac/__init__.py index ef534de1..a4aa157a 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac/__init__.py @@ -61,7 +61,7 @@ def init(node_tree): sec_uv_n = node_tree.nodes.new("ShaderNodeUVMap") sec_uv_n.name = DifSpecOverDifOpac.SEC_UVMAP_NODE sec_uv_n.label = DifSpecOverDifOpac.SEC_UVMAP_NODE - sec_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1200) + sec_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) sec_uv_n.uv_map = _MESH_consts.none_uv over_tex_n = node_tree.nodes.new("ShaderNodeTexImage") @@ -70,24 +70,25 @@ def init(node_tree): over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) over_tex_n.width = 140 - over_mix_node = node_tree.nodes.new("ShaderNodeMixRGB") + over_mix_node = node_tree.nodes.new("ShaderNodeMix") over_mix_node.name = DifSpecOverDifOpac.OVER_MIX_NODE over_mix_node.label = DifSpecOverDifOpac.OVER_MIX_NODE - over_mix_node.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1300) + over_mix_node.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1350) + over_mix_node.data_type = "RGBA" over_mix_node.blend_type = "MIX" # links creation node_tree.links.new(over_tex_n.inputs['Vector'], sec_uv_n.outputs['UV']) - node_tree.links.new(over_mix_node.inputs['Fac'], over_tex_n.outputs['Alpha']) - node_tree.links.new(over_mix_node.inputs['Color1'], base_tex_n.outputs['Color']) - node_tree.links.new(over_mix_node.inputs['Color2'], over_tex_n.outputs['Color']) + node_tree.links.new(over_mix_node.inputs['Factor'], over_tex_n.outputs['Alpha']) + node_tree.links.new(over_mix_node.inputs['A'], base_tex_n.outputs['Color']) + node_tree.links.new(over_mix_node.inputs['B'], over_tex_n.outputs['Color']) - node_tree.links.new(vcol_mult_n.inputs[1], over_mix_node.outputs['Color']) + node_tree.links.new(vcol_mult_n.inputs[1], over_mix_node.outputs['Result']) @staticmethod def set_aux1(node_tree, aux_property): - """Set second texture generation scale. + """Set second texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -96,8 +97,11 @@ def set_aux1(node_tree, aux_property): """ if tg1.is_set(node_tree): - - tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) @staticmethod def set_reflection2(node_tree, value): diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac_add_env/__init__.py new file mode 100644 index 00000000..70140cfd --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_over_dif_opac_add_env/__init__.py @@ -0,0 +1,46 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.internals.shaders.eut2.dif_spec_over_dif_opac import DifSpecOverDifOpac +from io_scs_tools.internals.shaders.eut2.std_passes.add_env import StdAddEnv + + +class DifSpecOverDifOpacAddEnv(DifSpecOverDifOpac, StdAddEnv): + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + # init parents + DifSpecOverDifOpac.init(node_tree) + StdAddEnv.add(node_tree, + DifSpecOverDifOpac.GEOM_NODE, + node_tree.nodes[DifSpecOverDifOpac.SPEC_COL_NODE].outputs['Color'], + node_tree.nodes[DifSpecOverDifOpac.BASE_TEX_NODE].outputs['Alpha'], + node_tree.nodes[DifSpecOverDifOpac.LIGHTING_EVAL_NODE].outputs['Normal'], + node_tree.nodes[DifSpecOverDifOpac.COMPOSE_LIGHTING_NODE].inputs['Env Color']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py index e1b63dcf..9e24a1fd 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/__init__.py @@ -20,6 +20,7 @@ from io_scs_tools.internals.shaders.eut2.dif_spec_weight import DifSpecWeight from io_scs_tools.internals.shaders.eut2.std_passes.add_env import StdAddEnv +from io_scs_tools.internals.shaders.eut2.dif_spec_weight_add_env import detail_nmap class DifSpecWeightAddEnv(DifSpecWeight, StdAddEnv): @@ -50,3 +51,64 @@ def init(node_tree): # links creation node_tree.links.new(add_env_gn.inputs['Weighted Color'], vcol_scale_n.outputs[0]) + + @staticmethod + def set_nmap2_flavor(node_tree, switch_on): + """Set secondary normal map flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if normal map should be switched on or off + :type switch_on: bool + """ + + if switch_on: + + # find minimal y position for input nodes and position flavor beneath it + min_y = None + for node in node_tree.nodes: + if node.location.x <= 185 and (min_y is None or min_y > node.location.y): + min_y = node.location.y + + lighting_eval_n = node_tree.nodes[DifSpecWeightAddEnv.LIGHTING_EVAL_NODE] + geom_n = node_tree.nodes[DifSpecWeightAddEnv.GEOM_NODE] + location = (lighting_eval_n.location.x - 185, min_y - 400) + + detail_nmap.init(node_tree, location, lighting_eval_n.inputs['Normal Vector'], geom_n.outputs['Normal']) + else: + detail_nmap.delete(node_tree) + + @staticmethod + def set_nmap_detail_uv(node_tree, uv_layer): + """Set UV layer to detail normal map texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for detail nmap texture + :type uv_layer: str + """ + + detail_nmap.set_detail_uv(node_tree, uv_layer) + + @staticmethod + def set_nmap_detail_texture(node_tree, texture): + """Set detail normal map texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param texture: texture which should be assigned to detail nmap texture node + :type texture: bpy.types.Texture + """ + + detail_nmap.set_detail_texture(node_tree, texture) + + @staticmethod + def set_nmap_detail_texture_settings(node_tree, settings): + """Set detail normal map texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + detail_nmap.set_detail_texture_settings(node_tree, settings) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/detail_nmap.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/detail_nmap.py new file mode 100644 index 00000000..1c340d85 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/detail_nmap.py @@ -0,0 +1,167 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +import bpy +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.utils import material as _material_utils +from io_scs_tools.internals.shaders.flavors import nmap + +DET_NMAP_UVMAP_NODE = "DetailNMapUVs" +DET_NMAP_TEX_NODE = "DetailNMapTex" +DET_NMAP_COMBINE_NODE = "DetailNMapCombine" + + +def __create_nodes__(node_tree, location): + """Create node for detail normal maps. + + :param node_tree: node tree on which normal map will be used + :type node_tree: bpy.types.NodeTree + """ + + frame = node_tree.nodes[nmap.NMAP_FLAVOR_FRAME_NODE] + nmap_uvs_n = node_tree.nodes[nmap.NMAP_UVMAP_NODE] + nmap_tex_n = node_tree.nodes[nmap.NMAP_TEX_NODE] + nmap_dds16_n = node_tree.nodes[nmap.NMAP_DDS16_GNODE] + nmap_scale_n = node_tree.nodes[nmap.NMAP_SCALE_GNODE] + + # move existing + nmap_uvs_n.location.x -= 185 + nmap_tex_n.location.x -= 185 + + # nodes creation + det_nmap_uvs_n = node_tree.nodes.new("ShaderNodeUVMap") + det_nmap_uvs_n.parent = frame + det_nmap_uvs_n.name = det_nmap_uvs_n.label = DET_NMAP_UVMAP_NODE + det_nmap_uvs_n.location = (location[0] - 185 * 5, location[1] - 400) + det_nmap_uvs_n.uv_map = _MESH_consts.none_uv + + det_nmap_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + det_nmap_tex_n.parent = frame + det_nmap_tex_n.name = det_nmap_tex_n.label = DET_NMAP_TEX_NODE + det_nmap_tex_n.location = (location[0] - 185 * 4, location[1] - 400) + det_nmap_tex_n.width = 140 + + det_nmap_combine_n = node_tree.nodes.new("ShaderNodeMix") + det_nmap_combine_n.parent = frame + det_nmap_combine_n.name = det_nmap_combine_n.label = DET_NMAP_COMBINE_NODE + det_nmap_combine_n.location = (location[0] - 185 * 3, location[1] - 110) + det_nmap_combine_n.data_type = "RGBA" + det_nmap_combine_n.blend_type = "OVERLAY" + det_nmap_combine_n.inputs["Factor"].default_value = 1.0 + + # links creation + node_tree.links.new(nmap_uvs_n.outputs["UV"], det_nmap_tex_n.inputs["Vector"]) + node_tree.links.new(det_nmap_uvs_n.outputs["UV"], nmap_tex_n.inputs["Vector"]) + + node_tree.links.new(nmap_tex_n.outputs["Color"], det_nmap_combine_n.inputs["A"]) + node_tree.links.new(det_nmap_tex_n.outputs["Color"], det_nmap_combine_n.inputs["B"]) + + node_tree.links.new(det_nmap_combine_n.outputs["Result"], nmap_dds16_n.inputs["Color"]) + node_tree.links.new(det_nmap_combine_n.outputs["Result"], nmap_scale_n.inputs["NMap Tex Color"]) + + +def init(node_tree, location, normal_to, normal_from): + """Initialize normal map nodes. + + :param node_tree: node tree on which normal map will be used + :type node_tree: bpy.types.NodeTree + :param location: x position in node tree + :type location: tuple[int, int] + :param normal_to: node socket to which result of normal map material should be send + :type normal_to: bpy.types.NodeSocket + :param normal_from: node socket from which original mesh normal should be taken + :type normal_from: bpy.types.NodeSocket + """ + + if nmap.NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: + nmap.init(node_tree, location, normal_to, normal_from) + __create_nodes__(node_tree, location) + + +def set_detail_texture(node_tree, image): + """Set texture to normal map flavor. + + :param node_tree: node tree on which normal map is used + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to nmap texture node + :type image: bpy.types.Texture + """ + + # save currently active node to properly reset it on the end + # without reset of active node this material is marked as active which we don't want + old_active = node_tree.nodes.active + + # ignore empty texture + if image is None: + delete(node_tree, True) + return + + # create material node if not yet created + if nmap.NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: + return + + # assign texture to texture node first + node_tree.nodes[DET_NMAP_TEX_NODE].image = image + + node_tree.nodes.active = old_active + + +def set_detail_texture_settings(node_tree, settings): + """Set detail normal map texture settings to flavor. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DET_NMAP_TEX_NODE], settings) + +def set_detail_uv(node_tree, uv_layer): + """Set UV layer to texture in normal map flavor. + + :param node_tree: node tree on which normal map is used + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + # set uv layer to texture node and normal map node + node_tree.nodes[DET_NMAP_UVMAP_NODE].uv_map = uv_layer + node_tree.nodes[nmap.NMAP_NODE].uv_map = uv_layer + + +def delete(node_tree, preserve_node=False): + """Delete normal map nodes from node tree. + + :param node_tree: node tree from which normal map should be deleted + :type node_tree: bpy.types.NodeTree + :param preserve_node: if true node won't be deleted + :type preserve_node: bool + """ + + if DET_NMAP_TEX_NODE in node_tree.nodes and not preserve_node: + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_UVMAP_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_TEX_NODE]) + node_tree.nodes.remove(node_tree.nodes[DET_NMAP_COMBINE_NODE]) + + nmap.delete(node_tree, preserve_node=preserve_node) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/nofresnel.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/nofresnel.py new file mode 100644 index 00000000..e95c1e84 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_add_env/nofresnel.py @@ -0,0 +1,53 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.internals.shaders.eut2.dif_spec_weight_add_env import DifSpecWeightAddEnv + + +class DifSpecWeightAddEnvNoFresnel(DifSpecWeightAddEnv): + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + # init parent + DifSpecWeightAddEnv.init(node_tree) + + # set fresnel factor to 0 + node_tree.nodes[DifSpecWeightAddEnv.ADD_ENV_GROUP_NODE].inputs['Apply Fresnel'].default_value = 0.0 + + @staticmethod + def set_fresnel(node_tree, bias_scale): + """Set fresnel bias and scale value to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param bias_scale: bias and scale factors as tuple: (bias, scale) + :type bias_scale: (float, float) + """ + pass # NOTE: fresnel is not supported on this shader so just skip it diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mask_dif_spec_weight/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mask_dif_spec_weight/__init__.py new file mode 100644 index 00000000..d2311367 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mask_dif_spec_weight/__init__.py @@ -0,0 +1,283 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.dif_spec import DifSpec +from io_scs_tools.utils import material as _material_utils + +class DifSpecWeightMaskDifSpecWeight(DifSpec): + SEC_UVMAP_NODE = "SecondUVMap" + THIRD_UVMAP_NODE = "ThirdUVMap" + MASK_TEX_NODE = "MaskTex" + OVER_TEX_NODE = "OverTex" + BASE_OVER_MIX_NODE = "BaseOverColorMix" + BASE_OVER_A_MIX_NODE = "BaseOverAlphaMix" + SEC_SPEC_COL_NODE = "SecSpecularColor" + SEC_SPEC_MIX_NODE = "SecSpecMix" + SEC_SHININESS_MIX_NODE = "SecShininnesMix" + VCOL_SPEC_MULT_NODE = "VColSpecMult" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + DifSpec.init(node_tree, disable_remap_alpha=True) + + base_tex_n = node_tree.nodes[DifSpec.BASE_TEX_NODE] + spec_col_n = node_tree.nodes[DifSpec.SPEC_COL_NODE] + spec_multi_n = node_tree.nodes[DifSpec.SPEC_MULT_NODE] + vcol_scale_n = node_tree.nodes[DifSpec.VCOLOR_SCALE_NODE] + vcol_multi_n = node_tree.nodes[DifSpec.VCOLOR_MULT_NODE] + lighting_eval_n = node_tree.nodes[DifSpec.LIGHTING_EVAL_NODE] + compose_lighting_n = node_tree.nodes[DifSpec.COMPOSE_LIGHTING_NODE] + output_n = node_tree.nodes[DifSpec.OUTPUT_NODE] + + # delete existing + node_tree.nodes.remove(node_tree.nodes[DifSpec.OPACITY_NODE]) + + # move existing + spec_multi_n.location.x += pos_x_shift + spec_multi_n.location.y += 100 + lighting_eval_n.location.x += pos_x_shift + compose_lighting_n.location.x += pos_x_shift + output_n.location.x += pos_x_shift + + # node creation + # - column -1 - + sec_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + sec_uv_n.name = sec_uv_n.label = DifSpecWeightMaskDifSpecWeight.SEC_UVMAP_NODE + sec_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1100) + sec_uv_n.uv_map = _MESH_consts.none_uv + + third_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + third_uv_n.name = third_uv_n.label = DifSpecWeightMaskDifSpecWeight.THIRD_UVMAP_NODE + third_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 900) + third_uv_n.uv_map = _MESH_consts.none_uv + + # - column 1 - + sec_spec_col_n = node_tree.nodes.new("ShaderNodeRGB") + sec_spec_col_n.name = sec_spec_col_n.label = DifSpecWeightMaskDifSpecWeight.SEC_SPEC_COL_NODE + sec_spec_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 2100) + + mask_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_tex_n.name = mask_tex_n.label = DifSpecWeightMaskDifSpecWeight.MASK_TEX_NODE + mask_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) + mask_tex_n.width = 140 + + over_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_tex_n.name = over_tex_n.label = DifSpecWeightMaskDifSpecWeight.OVER_TEX_NODE + over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) + over_tex_n.width = 140 + + # - column 3 - + sec_shininess_mix_n = node_tree.nodes.new("ShaderNodeMix") + sec_shininess_mix_n.name = sec_shininess_mix_n.label = DifSpecWeightMaskDifSpecWeight.SEC_SHININESS_MIX_NODE + sec_shininess_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2350) + sec_shininess_mix_n.data_type = "RGBA" + sec_shininess_mix_n.blend_type = "MIX" + + sec_spec_mix_n = node_tree.nodes.new("ShaderNodeMix") + sec_spec_mix_n.name = sec_spec_mix_n.label = DifSpecWeightMaskDifSpecWeight.SEC_SPEC_MIX_NODE + sec_spec_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2100) + sec_spec_mix_n.data_type = "RGBA" + sec_spec_mix_n.blend_type = "MIX" + + base_over_a_mix_n = node_tree.nodes.new("ShaderNodeMix") + base_over_a_mix_n.name = base_over_a_mix_n.label = DifSpecWeightMaskDifSpecWeight.BASE_OVER_A_MIX_NODE + base_over_a_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1850) + base_over_a_mix_n.data_type = "RGBA" + base_over_a_mix_n.blend_type = "MIX" + + base_over_mix_n = node_tree.nodes.new("ShaderNodeMix") + base_over_mix_n.name = base_over_mix_n.label = DifSpecWeightMaskDifSpecWeight.BASE_OVER_MIX_NODE + base_over_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1350) + base_over_mix_n.data_type = "RGBA" + base_over_mix_n.blend_type = "MIX" + + # - column 5 - + vcol_spec_mul_n = node_tree.nodes.new("ShaderNodeVectorMath") + vcol_spec_mul_n.name = vcol_spec_mul_n.label = DifSpecWeightMaskDifSpecWeight.VCOL_SPEC_MULT_NODE + vcol_spec_mul_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 1900) + vcol_spec_mul_n.operation = "MULTIPLY" + + # links creation + # - column -1 - + node_tree.links.new(sec_uv_n.outputs['UV'], mask_tex_n.inputs['Vector']) + node_tree.links.new(third_uv_n.outputs['UV'], over_tex_n.inputs['Vector']) + + # - column 1 - + node_tree.links.new(sec_spec_col_n.outputs['Color'], sec_spec_mix_n.inputs['B']) + + node_tree.links.new(spec_col_n.outputs['Color'], sec_spec_mix_n.inputs['A']) + + node_tree.links.new(base_tex_n.outputs['Color'], base_over_mix_n.inputs['A']) + node_tree.links.new(base_tex_n.outputs['Alpha'], base_over_a_mix_n.inputs['A']) + + node_tree.links.new(mask_tex_n.outputs['Color'], sec_shininess_mix_n.inputs['Factor']) + node_tree.links.new(mask_tex_n.outputs['Color'], sec_spec_mix_n.inputs['Factor']) + node_tree.links.new(mask_tex_n.outputs['Color'], base_over_a_mix_n.inputs['Factor']) + node_tree.links.new(mask_tex_n.outputs['Color'], base_over_mix_n.inputs['Factor']) + + node_tree.links.new(over_tex_n.outputs['Color'], base_over_mix_n.inputs['B']) + node_tree.links.new(over_tex_n.outputs['Alpha'], base_over_a_mix_n.inputs['A']) + + # - column 3 - + node_tree.links.new(sec_shininess_mix_n.outputs['Result'], lighting_eval_n.inputs['Shininess']) + node_tree.links.new(sec_spec_mix_n.outputs['Result'], spec_multi_n.inputs[0]) + node_tree.links.new(base_over_mix_n.outputs['Result'], spec_multi_n.inputs[1]) + node_tree.links.new(vcol_scale_n.outputs[0], vcol_spec_mul_n.inputs[1]) + node_tree.links.new(base_over_mix_n.outputs['Result'], vcol_multi_n.inputs[1]) + + # - column 4 - + node_tree.links.new(spec_multi_n.outputs['Vector'], vcol_spec_mul_n.inputs[0]) + + # - column 5 - + node_tree.links.new(vcol_spec_mul_n.outputs['Vector'], compose_lighting_n.inputs['Specular Color']) + + @staticmethod + def set_shininess(node_tree, factor): + """Set shininess factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param factor: shininess factor + :type factor: float + """ + + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.SEC_SHININESS_MIX_NODE].inputs["A"].default_value = (factor,) * 4 + + @staticmethod + def set_mask_texture(node_tree, image): + """Set mask texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mask texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.MASK_TEX_NODE].image = image + + @staticmethod + def set_mask_texture_settings(node_tree, settings): + """Set mask texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecWeightMaskDifSpecWeight.MASK_TEX_NODE], settings) + + @staticmethod + def set_mask_uv(node_tree, uv_layer): + """Set UV layer to mask texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mask texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.SEC_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_over_texture(node_tree, image): + """Set over texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to over texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.OVER_TEX_NODE].image = image + + @staticmethod + def set_over_texture_settings(node_tree, settings): + """Set over texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecWeightMaskDifSpecWeight.OVER_TEX_NODE], settings) + + @staticmethod + def set_over_uv(node_tree, uv_layer): + """Set UV layer to over texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.THIRD_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_aux3(node_tree, aux_property): + """Set secondary specular color to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: secondary specular color represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + color = (aux_property[0]["value"], aux_property[1]["value"], aux_property[2]["value"], 1.0) + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.SEC_SPEC_COL_NODE].outputs["Color"].default_value = color + + factor = aux_property[3]["value"] + node_tree.nodes[DifSpecWeightMaskDifSpecWeight.SEC_SHININESS_MIX_NODE].inputs["B"].default_value = (factor,) * 4 + + @staticmethod + def set_reflection2(node_tree, value): + """Set second reflection factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param value: reflection factor + :type value: float + """ + + pass # NOTE: reflection attribute doesn't change anything in rendered material, so pass it@staticmethod \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2/__init__.py index 3522eefd..25642f3d 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2/__init__.py @@ -191,7 +191,7 @@ def set_tg0_flavor(node_tree, switch_on): @staticmethod def set_aux0(node_tree, aux_property): - """Set zero texture generation scale. + """Set zero texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -200,5 +200,8 @@ def set_aux0(node_tree, aux_property): """ if tg0.is_set(node_tree): - - tg0.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + # Fix for old float2 aux[0] + if (len(aux_property)) == 2: + tg0.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg0.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_mask2/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_mask2/__init__.py new file mode 100644 index 00000000..b25c463d --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_mask2/__init__.py @@ -0,0 +1,403 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.dif_spec_weight_mult2 import DifSpecWeightMult2 +from io_scs_tools.internals.shaders.eut2.std_node_groups import mult2_mix_ng +from io_scs_tools.internals.shaders.flavors import tg1 +from io_scs_tools.utils import material as _material_utils + +class DifSpecWeightMult2Mask2(DifSpecWeightMult2): + SEC_UVMAP_NODE = "SecondUVMap" + THIRD_UVMAP_NODE = "ThirdUVMap" + SEC_UV_SCALE_NODE = "SecUVScale" + SEC_SPEC_COLOR_NODE = "SecSpecularColor" + SEC_SHININESS_MIX_NODE = "SecShininnesMix" + SPEC_COLOR_MIX_NODE = "SpecularColorMix" + BASE_1_TEX_NODE = "Base1Tex" + MULT_1_TEX_NODE = "Mult1Tex" + MASK_TEX_NODE = "MaskTex" + SEC_MULT2_MIX_GROUP_NODE = "SecMult2MixGroup" + COMBINED_ALPHA_MIX_NODE = "CombinedAlphaMixes" + COMBINED_MIX_NODE = "CombinedMixes" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + DifSpecWeightMult2.init(node_tree) + + spec_col_n = node_tree.nodes[DifSpecWeightMult2.SPEC_COL_NODE] + mult2_mix_gn = node_tree.nodes[DifSpecWeightMult2.MULT2_MIX_GROUP_NODE] + spec_mult_n = node_tree.nodes[DifSpecWeightMult2.SPEC_MULT_NODE] + vcol_mult_n = node_tree.nodes[DifSpecWeightMult2.VCOLOR_MULT_NODE] + opacity_mult_n = node_tree.nodes[DifSpecWeightMult2.OPACITY_NODE] + lighting_eval_n = node_tree.nodes[DifSpecWeightMult2.LIGHTING_EVAL_NODE] + + # delete existing + node_tree.nodes.remove(opacity_mult_n) + + # move existing + for node in node_tree.nodes: + if node.location.x > start_pos_x + pos_x_shift * 3: + node.location.x += pos_x_shift + + # nodes creation + # - column -1 - + sec_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + sec_uv_n.name = sec_uv_n.label = DifSpecWeightMult2Mask2.SEC_UVMAP_NODE + sec_uv_n.location = (start_pos_x - pos_x_shift * 3, start_pos_y + 600) + sec_uv_n.uv_map = _MESH_consts.none_uv + + third_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + third_uv_n.name = third_uv_n.label = DifSpecWeightMult2Mask2.THIRD_UVMAP_NODE + third_uv_n.location = (start_pos_x - pos_x_shift * 3, start_pos_y + 200) + third_uv_n.uv_map = _MESH_consts.none_uv + + # - column 1 - + sec_uv_scale_n = node_tree.nodes.new("ShaderNodeMapping") + sec_uv_scale_n.name = sec_uv_scale_n.label = DifSpecWeightMult2Mask2.SEC_UV_SCALE_NODE + sec_uv_scale_n.location = (start_pos_x - pos_x_shift, start_pos_y + 600) + sec_uv_scale_n.vector_type = "POINT" + sec_uv_scale_n.inputs['Location'].default_value = sec_uv_scale_n.inputs['Rotation'].default_value = (0.0,) * 3 + sec_uv_scale_n.inputs['Scale'].default_value = (1.0,) * 3 + sec_uv_scale_n.width = 140 + + # - column 3 - + sec_spec_col_n = node_tree.nodes.new("ShaderNodeRGB") + sec_spec_col_n.name = sec_spec_col_n.label = DifSpecWeightMult2Mask2.SEC_SPEC_COLOR_NODE + sec_spec_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 2100) + + # - column 4 - + base_1_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + base_1_tex_n.name = base_1_tex_n.label = DifSpecWeightMult2Mask2.BASE_1_TEX_NODE + base_1_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) + base_1_tex_n.width = 140 + + mult_1_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mult_1_tex_n.name = mult_1_tex_n.label = DifSpecWeightMult2Mask2.MULT_1_TEX_NODE + mult_1_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 600) + mult_1_tex_n.width = 140 + + mask_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_tex_n.name = mask_tex_n.label = DifSpecWeightMult2Mask2.MASK_TEX_NODE + mask_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 300) + mask_tex_n.width = 140 + + # - column 5 - + sec_shininess_mix_n = node_tree.nodes.new("ShaderNodeMix") + sec_shininess_mix_n.name = sec_shininess_mix_n.label = DifSpecWeightMult2Mask2.SEC_SHININESS_MIX_NODE + sec_shininess_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2400) + sec_shininess_mix_n.data_type = "RGBA" + sec_shininess_mix_n.blend_type = "MIX" + + spec_col_mix_n = node_tree.nodes.new("ShaderNodeMix") + spec_col_mix_n.name = spec_col_mix_n.label = DifSpecWeightMult2Mask2.SPEC_COLOR_MIX_NODE + spec_col_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2150) + spec_col_mix_n.data_type = "RGBA" + spec_col_mix_n.blend_type = "MIX" + + sec_mult2_mix_gn = node_tree.nodes.new("ShaderNodeGroup") + sec_mult2_mix_gn.name = sec_mult2_mix_gn.label = DifSpecWeightMult2Mask2.SEC_MULT2_MIX_GROUP_NODE + sec_mult2_mix_gn.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 800) + sec_mult2_mix_gn.node_tree = mult2_mix_ng.get_node_group() + + # - column 6 - + combined_a_mix_n = node_tree.nodes.new("ShaderNodeMix") + combined_a_mix_n.name = combined_a_mix_n.label = DifSpecWeightMult2Mask2.COMBINED_ALPHA_MIX_NODE + combined_a_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1250) + combined_a_mix_n.data_type = "RGBA" + combined_a_mix_n.blend_type = "MIX" + + combined_mix_n = node_tree.nodes.new("ShaderNodeMix") + combined_mix_n.name = combined_mix_n.label = DifSpecWeightMult2Mask2.COMBINED_MIX_NODE + combined_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1000) + combined_mix_n.data_type = "RGBA" + combined_mix_n.blend_type = "MIX" + + # links creation + # - column -1 - + node_tree.links.new(sec_uv_n.outputs["UV"], base_1_tex_n.inputs["Vector"]) + node_tree.links.new(sec_uv_n.outputs["UV"], sec_uv_scale_n.inputs["Vector"]) + + node_tree.links.new(third_uv_n.outputs["UV"], mask_tex_n.inputs["Vector"]) + + # - column 1 - + node_tree.links.new(sec_uv_scale_n.outputs["Vector"], mult_1_tex_n.inputs["Vector"]) + + # - column 3 - + node_tree.links.new(sec_spec_col_n.outputs["Color"], spec_col_mix_n.inputs["B"]) + node_tree.links.new(spec_col_n.outputs["Color"], spec_col_mix_n.inputs["A"]) + + node_tree.links.new(base_1_tex_n.outputs["Color"], sec_mult2_mix_gn.inputs["Base Color"]) + node_tree.links.new(base_1_tex_n.outputs["Alpha"], sec_mult2_mix_gn.inputs["Base Alpha"]) + + node_tree.links.new(mult_1_tex_n.outputs["Color"], sec_mult2_mix_gn.inputs["Mult Color"]) + node_tree.links.new(mult_1_tex_n.outputs["Alpha"], sec_mult2_mix_gn.inputs["Mult Alpha"]) + + node_tree.links.new(mask_tex_n.outputs["Color"], sec_shininess_mix_n.inputs["Factor"]) + node_tree.links.new(mask_tex_n.outputs["Color"], spec_col_mix_n.inputs["Factor"]) + node_tree.links.new(mask_tex_n.outputs["Color"], combined_a_mix_n.inputs["Factor"]) + node_tree.links.new(mask_tex_n.outputs["Color"], combined_mix_n.inputs["Factor"]) + + # - column 5 - + node_tree.links.new(sec_shininess_mix_n.outputs["Result"], lighting_eval_n.inputs["Shininess"]) + node_tree.links.new(spec_col_mix_n.outputs["Result"], spec_mult_n.inputs[0]) + + node_tree.links.new(mult2_mix_gn.outputs["Mix Alpha"], combined_a_mix_n.inputs["A"]) + node_tree.links.new(mult2_mix_gn.outputs["Mix Color"], combined_mix_n.inputs["A"]) + + node_tree.links.new(sec_mult2_mix_gn.outputs["Mix Alpha"], combined_a_mix_n.inputs["B"]) + node_tree.links.new(sec_mult2_mix_gn.outputs["Mix Color"], combined_mix_n.inputs["B"]) + + # - column 6 - + node_tree.links.new(combined_a_mix_n.outputs["Result"], spec_mult_n.inputs[1]) + node_tree.links.new(combined_mix_n.outputs["Result"], vcol_mult_n.inputs[1]) + + @staticmethod + def set_shininess(node_tree, factor): + """Set shininess factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param factor: shininess factor + :type factor: float + """ + + node_tree.nodes[DifSpecWeightMult2Mask2.SEC_SHININESS_MIX_NODE].inputs["A"].default_value = (factor,) * 4 + + @staticmethod + def set_reflection2(node_tree, value): + """Set second reflection. + + NOTE: just passed because it's not reflected in 3D viewport anyway + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param value: reflection value + :type value: float + """ + pass + + @staticmethod + def set_aux3(node_tree, aux_property): + """Set second specular and second shininess for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: second specular and shininess factor represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + color = (aux_property[0]["value"], aux_property[1]["value"], aux_property[2]["value"], 1.0) + node_tree.nodes[DifSpecWeightMult2Mask2.SEC_SPEC_COLOR_NODE].outputs["Color"].default_value = color + + factor = aux_property[3]["value"] + node_tree.nodes[DifSpecWeightMult2Mask2.SEC_SHININESS_MIX_NODE].inputs["B"].default_value = (factor,) * 4 + + @staticmethod + def set_aux5(node_tree, aux_property): + """Set UV scaling factors for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: UV scale factor represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + DifSpecWeightMult2.set_aux5(node_tree, aux_property) + + node_tree.nodes[DifSpecWeightMult2Mask2.SEC_UV_SCALE_NODE].inputs['Scale'].default_value[0] = aux_property[2]["value"] + node_tree.nodes[DifSpecWeightMult2Mask2.SEC_UV_SCALE_NODE].inputs['Scale'].default_value[1] = aux_property[3]["value"] + + @staticmethod + def set_base_1_texture(node_tree, image): + """Set base_1 texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to base_1 texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecWeightMult2Mask2.BASE_1_TEX_NODE].image = image + + @staticmethod + def set_base_1_texture_settings(node_tree, settings): + """Set base_1 texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecWeightMult2Mask2.BASE_1_TEX_NODE], settings) + + @staticmethod + def set_base_1_uv(node_tree, uv_layer): + """Set UV layer to base_1 texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base_1 texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecWeightMult2Mask2.SEC_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_mult_1_texture(node_tree, image): + """Set mult_1 texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mult_1 texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecWeightMult2Mask2.MULT_1_TEX_NODE].image = image + + @staticmethod + def set_mult_1_texture_settings(node_tree, settings): + """Set mult_1 texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecWeightMult2Mask2.MULT_1_TEX_NODE], settings) + + @staticmethod + def set_mult_1_uv(node_tree, uv_layer): + """Set UV layer to mult_1 texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mult_1 texture + :type uv_layer: str + """ + + DifSpecWeightMult2Mask2.set_base_1_uv(node_tree, uv_layer) + + @staticmethod + def set_mask_texture(node_tree, image): + """Set mask texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mask texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[DifSpecWeightMult2Mask2.MASK_TEX_NODE].image = image + + @staticmethod + def set_mask_texture_settings(node_tree, settings): + """Set mask texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[DifSpecWeightMult2Mask2.MASK_TEX_NODE], settings) + + @staticmethod + def set_mask_uv(node_tree, uv_layer): + """Set UV layer to mask texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mask texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[DifSpecWeightMult2Mask2.THIRD_UVMAP_NODE].uv_map = uv_layer + + + + + + + + @staticmethod + def set_tg1_flavor(node_tree, switch_on): + """Set zero texture generation flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + if switch_on and not tg1.is_set(node_tree): + + out_node = node_tree.nodes[DifSpecWeightMult2Mask2.GEOM_NODE] + in_node = node_tree.nodes[DifSpecWeightMult2Mask2.SEC_UV_SCALE_NODE] + in_node2 = node_tree.nodes[DifSpecWeightMult2Mask2.BASE_1_TEX_NODE] + + out_node.location.x -= 185 * 2 + location = (out_node.location.x + 185, out_node.location.y) + + tg1.init(node_tree, location, out_node.outputs["Position"], in_node.inputs["Vector"]) + tg1.init(node_tree, location, out_node.outputs["Position"], in_node2.inputs["Vector"]) + + elif not switch_on: + + tg1.delete(node_tree) + + @staticmethod + def set_aux1(node_tree, aux_property): + """Set second texture generation scale and rotation. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: secondary specular color represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + if tg1.is_set(node_tree): + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_weight2/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_weight2/__init__.py index d3089761..01fc6133 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_weight2/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_mult2_weight2/__init__.py @@ -22,7 +22,6 @@ from io_scs_tools.internals.shaders.eut2.dif_spec_weight_mult2 import DifSpecWeightMult2 from io_scs_tools.internals.shaders.eut2.std_node_groups import mult2_mix_ng from io_scs_tools.internals.shaders.flavors import tg1 -from io_scs_tools.utils import convert as _convert_utils from io_scs_tools.utils import material as _material_utils @@ -30,6 +29,7 @@ class DifSpecWeightMult2Weight2(DifSpecWeightMult2): SEC_UVMAP_NODE = "ThrdGeometry" SEC_UV_SCALE_NODE = "SecUVScale" SEC_SPEC_COLOR_NODE = "SecSpecularColor" + SEC_SHININESS_MIX_NODE = "SecShininnesMix" SPEC_COLOR_MIX_NODE = "SpecularColorMix" BASE_1_TEX_NODE = "Base1Tex" MULT_1_TEX_NODE = "Mult1Tex" @@ -46,10 +46,6 @@ def get_name(): def init(node_tree): """Initialize node tree with links for this shader. - NOTE: shininess is not set properly as we can't set shininess on material - via node system. So currently only primary shininess is taken into account, - but should be: lerp(shininnes, aux3[3], v_color_alpha) - :param node_tree: node tree on which this shader should be created :type node_tree: bpy.types.NodeTree """ @@ -70,6 +66,7 @@ def init(node_tree): spec_mult_n = node_tree.nodes[DifSpecWeightMult2.SPEC_MULT_NODE] vcol_mult_n = node_tree.nodes[DifSpecWeightMult2.VCOLOR_MULT_NODE] opacity_mult_n = node_tree.nodes[DifSpecWeightMult2.OPACITY_NODE] + lighting_eval_n = node_tree.nodes[DifSpecWeightMult2.LIGHTING_EVAL_NODE] # delete existing node_tree.nodes.remove(opacity_mult_n) @@ -97,6 +94,12 @@ def init(node_tree): sec_spec_col_n.name = sec_spec_col_n.label = DifSpecWeightMult2Weight2.SEC_SPEC_COLOR_NODE sec_spec_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 2100) + sec_shininess_mix_n = node_tree.nodes.new("ShaderNodeMix") + sec_shininess_mix_n.name = sec_shininess_mix_n.label = DifSpecWeightMult2Weight2.SEC_SHININESS_MIX_NODE + sec_shininess_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2400) + sec_shininess_mix_n.data_type = "RGBA" + sec_shininess_mix_n.blend_type = "MIX" + base_1_tex_n = node_tree.nodes.new("ShaderNodeTexImage") base_1_tex_n.name = base_1_tex_n.label = DifSpecWeightMult2Weight2.BASE_1_TEX_NODE base_1_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) @@ -107,9 +110,10 @@ def init(node_tree): mult_1_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 600) mult_1_tex_n.width = 140 - spec_col_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + spec_col_mix_n = node_tree.nodes.new("ShaderNodeMix") spec_col_mix_n.name = spec_col_mix_n.label = DifSpecWeightMult2Weight2.SPEC_COLOR_MIX_NODE - spec_col_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2100) + spec_col_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2150) + spec_col_mix_n.data_type = "RGBA" spec_col_mix_n.blend_type = "MIX" sec_mult2_mix_n = node_tree.nodes.new("ShaderNodeGroup") @@ -117,14 +121,16 @@ def init(node_tree): sec_mult2_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 800) sec_mult2_mix_n.node_tree = mult2_mix_ng.get_node_group() - combined_a_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + combined_a_mix_n = node_tree.nodes.new("ShaderNodeMix") combined_a_mix_n.name = combined_a_mix_n.label = DifSpecWeightMult2Weight2.COMBINED_ALPHA_MIX_NODE - combined_a_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1200) + combined_a_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1250) + combined_a_mix_n.data_type = "RGBA" combined_a_mix_n.blend_type = "MIX" - combined_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + combined_mix_n = node_tree.nodes.new("ShaderNodeMix") combined_mix_n.name = combined_mix_n.label = DifSpecWeightMult2Weight2.COMBINED_MIX_NODE combined_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1000) + combined_mix_n.data_type = "RGBA" combined_mix_n.blend_type = "MIX" # links creation @@ -135,9 +141,11 @@ def init(node_tree): node_tree.links.new(base_1_tex_n.inputs["Vector"], sec_uv_n.outputs["UV"]) # pass 1 - node_tree.links.new(spec_col_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(spec_col_mix_n.inputs["Color1"], spec_col_n.outputs["Color"]) - node_tree.links.new(spec_col_mix_n.inputs["Color2"], sec_spec_col_n.outputs["Color"]) + node_tree.links.new(sec_shininess_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + + node_tree.links.new(spec_col_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(spec_col_mix_n.inputs["A"], spec_col_n.outputs["Color"]) + node_tree.links.new(spec_col_mix_n.inputs["B"], sec_spec_col_n.outputs["Color"]) node_tree.links.new(sec_mult2_mix_n.inputs["Base Alpha"], base_1_tex_n.outputs["Alpha"]) node_tree.links.new(sec_mult2_mix_n.inputs["Base Color"], base_1_tex_n.outputs["Color"]) @@ -145,19 +153,34 @@ def init(node_tree): node_tree.links.new(sec_mult2_mix_n.inputs["Mult Color"], mult_1_tex_n.outputs["Color"]) # pass 2 - node_tree.links.new(combined_a_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(combined_a_mix_n.inputs["Color1"], mult2_mix_gn.outputs["Mix Alpha"]) - node_tree.links.new(combined_a_mix_n.inputs["Color2"], sec_mult2_mix_n.outputs["Mix Alpha"]) + node_tree.links.new(combined_a_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(combined_a_mix_n.inputs["A"], mult2_mix_gn.outputs["Mix Alpha"]) + node_tree.links.new(combined_a_mix_n.inputs["B"], sec_mult2_mix_n.outputs["Mix Alpha"]) - node_tree.links.new(combined_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(combined_mix_n.inputs["Color1"], mult2_mix_gn.outputs["Mix Color"]) - node_tree.links.new(combined_mix_n.inputs["Color2"], sec_mult2_mix_n.outputs["Mix Color"]) + node_tree.links.new(combined_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(combined_mix_n.inputs["A"], mult2_mix_gn.outputs["Mix Color"]) + node_tree.links.new(combined_mix_n.inputs["B"], sec_mult2_mix_n.outputs["Mix Color"]) # pass 3 - node_tree.links.new(spec_mult_n.inputs[0], spec_col_mix_n.outputs["Color"]) - node_tree.links.new(spec_mult_n.inputs[1], combined_a_mix_n.outputs["Color"]) + node_tree.links.new(spec_mult_n.inputs[0], spec_col_mix_n.outputs["Result"]) + node_tree.links.new(spec_mult_n.inputs[1], combined_a_mix_n.outputs["Result"]) + + node_tree.links.new(vcol_mult_n.inputs[1], combined_mix_n.outputs["Result"]) + + # pass 4 + node_tree.links.new(lighting_eval_n.inputs["Shininess"], sec_shininess_mix_n.outputs["Result"]) + + @staticmethod + def set_shininess(node_tree, factor): + """Set shininess factor to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param factor: shininess factor + :type factor: float + """ - node_tree.links.new(vcol_mult_n.inputs[1], combined_mix_n.outputs["Color"]) + node_tree.nodes[DifSpecWeightMult2Weight2.SEC_SHININESS_MIX_NODE].inputs["A"].default_value = (factor,) * 4 @staticmethod def set_reflection2(node_tree, value): @@ -178,13 +201,15 @@ def set_aux3(node_tree, aux_property): :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree - :param aux_property: second specular and skininess factor represented with property group + :param aux_property: second specular and shininess factor represented with property group :type aux_property: bpy.types.IDPropertyGroup """ - color = _convert_utils.aux_to_node_color(aux_property) + color = (aux_property[0]["value"], aux_property[1]["value"], aux_property[2]["value"], 1.0) + node_tree.nodes[DifSpecWeightMult2Weight2.SEC_SPEC_COLOR_NODE].outputs["Color"].default_value = color - node_tree.nodes[DifSpecWeightMult2Weight2.SEC_SPEC_COLOR_NODE].outputs[0].default_value = color + factor = aux_property[3]["value"] + node_tree.nodes[DifSpecWeightMult2Weight2.SEC_SHININESS_MIX_NODE].inputs["B"].default_value = (factor,) * 4 @staticmethod def set_aux5(node_tree, aux_property): @@ -362,7 +387,7 @@ def set_tg1_flavor(node_tree, switch_on): @staticmethod def set_aux1(node_tree, aux_property): - """Set second texture generation scale. + """Set second texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -371,5 +396,8 @@ def set_aux1(node_tree, aux_property): """ if tg1.is_set(node_tree): - - tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py index 43c8bfa7..b7b7d17d 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/__init__.py @@ -20,6 +20,7 @@ from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.eut2.dif_spec import DifSpec +from io_scs_tools.internals.shaders.eut2.dif_spec_weight_weight_dif_spec_weight import over_nmap from io_scs_tools.internals.shaders.flavors import tg1 from io_scs_tools.utils import material as _material_utils @@ -90,24 +91,29 @@ def init(node_tree): over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) over_tex_n.width = 140 - sec_spec_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + sec_spec_mix_n = node_tree.nodes.new("ShaderNodeMix") sec_spec_mix_n.name = sec_spec_mix_n.label = DifSpecWeightWeightDifSpecWeight.SEC_SPEC_MIX_NODE sec_spec_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2100) + sec_spec_mix_n.data_type = "RGBA" sec_spec_mix_n.blend_type = "MIX" - sec_shininess_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + sec_shininess_mix_n = node_tree.nodes.new("ShaderNodeMix") sec_shininess_mix_n.name = sec_shininess_mix_n.label = DifSpecWeightWeightDifSpecWeight.SEC_SHININESS_MIX_NODE - sec_shininess_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2300) + sec_shininess_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 2350) + sec_shininess_mix_n.data_type = "RGBA" sec_shininess_mix_n.blend_type = "MIX" - base_over_a_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_over_a_mix_n = node_tree.nodes.new("ShaderNodeMix") base_over_a_mix_n.name = base_over_a_mix_n.label = DifSpecWeightWeightDifSpecWeight.BASE_OVER_A_MIX_NODE - base_over_a_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1900) + base_over_a_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1850) + base_over_a_mix_n.data_type = "RGBA" base_over_a_mix_n.blend_type = "MIX" - base_over_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_over_mix_n = node_tree.nodes.new("ShaderNodeMix") base_over_mix_n.name = base_over_mix_n.label = DifSpecWeightWeightDifSpecWeight.BASE_OVER_MIX_NODE base_over_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1350) + base_over_mix_n.data_type = "RGBA" + base_over_mix_n.blend_type = "MIX" vcol_spec_mul_n = node_tree.nodes.new("ShaderNodeVectorMath") vcol_spec_mul_n.name = vcol_spec_mul_n.label = DifSpecWeightWeightDifSpecWeight.VCOL_SPEC_MULT_NODE @@ -118,37 +124,100 @@ def init(node_tree): node_tree.links.new(over_tex_n.inputs["Vector"], sec_geom_n.outputs["UV"]) # pass 1 - node_tree.links.new(sec_shininess_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(sec_shininess_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(sec_spec_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(sec_spec_mix_n.inputs["Color1"], spec_col_n.outputs["Color"]) - node_tree.links.new(sec_spec_mix_n.inputs["Color2"], sec_spec_col_n.outputs["Color"]) + node_tree.links.new(sec_spec_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(sec_spec_mix_n.inputs["A"], spec_col_n.outputs["Color"]) + node_tree.links.new(sec_spec_mix_n.inputs["B"], sec_spec_col_n.outputs["Color"]) - node_tree.links.new(base_over_a_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(base_over_a_mix_n.inputs["Color1"], base_tex_n.outputs["Alpha"]) - node_tree.links.new(base_over_a_mix_n.inputs["Color2"], over_tex_n.outputs["Alpha"]) + node_tree.links.new(base_over_a_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(base_over_a_mix_n.inputs["A"], base_tex_n.outputs["Alpha"]) + node_tree.links.new(base_over_a_mix_n.inputs["B"], over_tex_n.outputs["Alpha"]) - node_tree.links.new(base_over_mix_n.inputs["Fac"], vcol_group_n.outputs["Vertex Color Alpha"]) - node_tree.links.new(base_over_mix_n.inputs["Color1"], base_tex_n.outputs["Color"]) - node_tree.links.new(base_over_mix_n.inputs["Color2"], over_tex_n.outputs["Color"]) + node_tree.links.new(base_over_mix_n.inputs["Factor"], vcol_group_n.outputs["Vertex Color Alpha"]) + node_tree.links.new(base_over_mix_n.inputs["A"], base_tex_n.outputs["Color"]) + node_tree.links.new(base_over_mix_n.inputs["B"], over_tex_n.outputs["Color"]) # pass 2 - node_tree.links.new(spec_multi_n.inputs[0], sec_spec_mix_n.outputs["Color"]) - node_tree.links.new(spec_multi_n.inputs[1], base_over_a_mix_n.outputs["Color"]) + node_tree.links.new(spec_multi_n.inputs[0], sec_spec_mix_n.outputs["Result"]) + node_tree.links.new(spec_multi_n.inputs[1], base_over_a_mix_n.outputs["Result"]) - node_tree.links.new(vcol_multi_n.inputs[1], base_over_mix_n.outputs["Color"]) + node_tree.links.new(vcol_multi_n.inputs[1], base_over_mix_n.outputs["Result"]) # pass 3 node_tree.links.new(vcol_spec_mul_n.inputs[0], spec_multi_n.outputs[0]) node_tree.links.new(vcol_spec_mul_n.inputs[1], vcol_scale_n.outputs[0]) # pass 4 - node_tree.links.new(lighting_eval_n.inputs["Shininess"], sec_shininess_mix_n.outputs["Color"]) + node_tree.links.new(lighting_eval_n.inputs["Shininess"], sec_shininess_mix_n.outputs["Result"]) # pass 5 node_tree.links.new(compose_lighting_n.inputs["Specular Color"], vcol_spec_mul_n.outputs[0]) node_tree.links.new(compose_lighting_n.inputs['Alpha'], base_tex_n.outputs['Alpha']) + + @staticmethod + def set_nmap_flavor(node_tree, switch_on): + """Set normal map flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if normal map should be switched on or off + :type switch_on: bool + """ + + if switch_on: + + # find minimal y position for input nodes and position flavor beneath it + min_y = None + for node in node_tree.nodes: + if node.location.x <= 185 and (min_y is None or min_y > node.location.y): + min_y = node.location.y + + lighting_eval_n = node_tree.nodes[DifSpecWeightWeightDifSpecWeight.LIGHTING_EVAL_NODE] + geom_n = node_tree.nodes[DifSpecWeightWeightDifSpecWeight.GEOM_NODE] + vcol_group_n = node_tree.nodes[DifSpecWeightWeightDifSpecWeight.VCOL_GROUP_NODE] + location = (lighting_eval_n.location.x - 185, min_y - 400) + + over_nmap.init(node_tree, location, lighting_eval_n.inputs['Normal Vector'], geom_n.outputs['Normal'], vcol_group_n.outputs["Vertex Color Alpha"]) + else: + over_nmap.delete(node_tree) + + @staticmethod + def set_nmap_over_uv(node_tree, uv_layer): + """Set UV layer to over normal map texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over nmap texture + :type uv_layer: str + """ + + over_nmap.set_over_uv(node_tree, uv_layer) + + @staticmethod + def set_nmap_over_texture(node_tree, texture): + """Set over normal map texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param texture: texture which should be assigned to over nmap texture node + :type texture: bpy.types.Texture + """ + + over_nmap.set_over_texture(node_tree, texture) + + @staticmethod + def set_nmap_over_texture_settings(node_tree, settings): + """Set over normal map texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + over_nmap.set_over_texture_settings(node_tree, settings) + @staticmethod def set_shininess(node_tree, factor): """Set shininess factor to shader. @@ -159,7 +228,7 @@ def set_shininess(node_tree, factor): :type factor: float """ - node_tree.nodes[DifSpecWeightWeightDifSpecWeight.SEC_SHININESS_MIX_NODE].inputs["Color1"].default_value = (factor,) * 4 + node_tree.nodes[DifSpecWeightWeightDifSpecWeight.SEC_SHININESS_MIX_NODE].inputs["A"].default_value = (factor,) * 4 @staticmethod def set_over_texture(node_tree, image): @@ -198,11 +267,11 @@ def set_over_uv(node_tree, uv_layer): @staticmethod def set_aux3(node_tree, aux_property): - """Set secondary specular color to shader. + """Set second specular and second shininess for the shader. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree - :param aux_property: secondary specular color represented with property group + :param aux_property: second specular and shininess factor represented with property group :type aux_property: bpy.types.IDPropertyGroup """ @@ -210,7 +279,7 @@ def set_aux3(node_tree, aux_property): node_tree.nodes[DifSpecWeightWeightDifSpecWeight.SEC_SPEC_COL_NODE].outputs["Color"].default_value = color factor = aux_property[3]["value"] - node_tree.nodes[DifSpecWeightWeightDifSpecWeight.SEC_SHININESS_MIX_NODE].inputs["Color2"].default_value = (factor,) * 4 + node_tree.nodes[DifSpecWeightWeightDifSpecWeight.SEC_SHININESS_MIX_NODE].inputs["B"].default_value = (factor,) * 4 @staticmethod def set_reflection2(node_tree, value): @@ -250,7 +319,7 @@ def set_tg1_flavor(node_tree, switch_on): @staticmethod def set_aux1(node_tree, aux_property): - """Set second texture generation scale. + """Set second texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -259,5 +328,8 @@ def set_aux1(node_tree, aux_property): """ if tg1.is_set(node_tree): - - tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/over_nmap.py b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/over_nmap.py new file mode 100644 index 00000000..659877c2 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_spec_weight_weight_dif_spec_weight/over_nmap.py @@ -0,0 +1,168 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +import bpy +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.utils import material as _material_utils +from io_scs_tools.internals.shaders.flavors import nmap + +OVER_NMAP_UVMAP_NODE = "OverNMapUVs" +OVER_NMAP_TEX_NODE = "OverNMapTex" +OVER_NMAP_COL_MIX_NODE = "OverNMapColMix" + + +def __create_nodes__(node_tree, location, factor_from): + """Create node for over normal maps. + + :param node_tree: node tree on which normal map will be used + :type node_tree: bpy.types.NodeTree + """ + + frame = node_tree.nodes[nmap.NMAP_FLAVOR_FRAME_NODE] + nmap_uvs_n = node_tree.nodes[nmap.NMAP_UVMAP_NODE] + nmap_tex_n = node_tree.nodes[nmap.NMAP_TEX_NODE] + nmap_dds16_n = node_tree.nodes[nmap.NMAP_DDS16_GNODE] + nmap_scale_n = node_tree.nodes[nmap.NMAP_SCALE_GNODE] + + # move existing + nmap_uvs_n.location.x -= 185 + nmap_tex_n.location.x -= 185 + + # nodes creation + over_nmap_uvs_n = node_tree.nodes.new("ShaderNodeUVMap") + over_nmap_uvs_n.parent = frame + over_nmap_uvs_n.name = over_nmap_uvs_n.label = OVER_NMAP_UVMAP_NODE + over_nmap_uvs_n.location = (location[0] - 185 * 5, location[1] - 400) + over_nmap_uvs_n.uv_map = _MESH_consts.none_uv + + over_nmap_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_nmap_tex_n.parent = frame + over_nmap_tex_n.name = over_nmap_tex_n.label = OVER_NMAP_TEX_NODE + over_nmap_tex_n.location = (location[0] - 185 * 4, location[1] - 400) + over_nmap_tex_n.width = 140 + + over_nmap_col_mix_n = node_tree.nodes.new("ShaderNodeMix") + over_nmap_col_mix_n.parent = frame + over_nmap_col_mix_n.name = over_nmap_col_mix_n.label = OVER_NMAP_COL_MIX_NODE + over_nmap_col_mix_n.location = (location[0] - 185 * 3, location[1] - 110) + over_nmap_col_mix_n.data_type = "RGBA" + over_nmap_col_mix_n.blend_type = "MIX" + + # links creation + node_tree.links.new(factor_from, over_nmap_col_mix_n.inputs["Factor"]) + + node_tree.links.new(over_nmap_uvs_n.outputs["UV"], over_nmap_tex_n.inputs["Vector"]) + + node_tree.links.new(nmap_tex_n.outputs["Color"], over_nmap_col_mix_n.inputs["A"]) + node_tree.links.new(over_nmap_tex_n.outputs["Color"], over_nmap_col_mix_n.inputs["B"]) + + node_tree.links.new(over_nmap_col_mix_n.outputs["Result"], nmap_dds16_n.inputs["Color"]) + node_tree.links.new(over_nmap_col_mix_n.outputs["Result"], nmap_scale_n.inputs["NMap Tex Color"]) + + +def init(node_tree, location, normal_to, normal_from, factor_from): + """Initialize normal map nodes. + + :param node_tree: node tree on which normal map will be used + :type node_tree: bpy.types.NodeTree + :param location: x position in node tree + :type location: tuple[int, int] + :param normal_to: node socket to which result of normal map material should be send + :type normal_to: bpy.types.NodeSocket + :param normal_from: node socket from which original mesh normal should be taken + :type normal_from: bpy.types.NodeSocket + :param factor_from: node socket from which factor for over blend should be taken + :type factor_from: bpy.types.NodeSocket + """ + + if nmap.NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: + nmap.init(node_tree, location, normal_to, normal_from) + __create_nodes__(node_tree, location, factor_from) + + +def set_over_texture(node_tree, image): + """Set over texture to normal map flavor. + + :param node_tree: node tree on which normal map is used + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to nmap texture node + :type image: bpy.types.Texture + """ + + # save currently active node to properly reset it on the end + # without reset of active node this material is marked as active which we don't want + old_active = node_tree.nodes.active + + # ignore empty texture + if image is None: + delete(node_tree, True) + return + + # create material node if not yet created + if nmap.NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: + return + + # assign texture to texture node first + node_tree.nodes[OVER_NMAP_TEX_NODE].image = image + + node_tree.nodes.active = old_active + + +def set_over_texture_settings(node_tree, settings): + """Set over normal map texture settings to flavor. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[OVER_NMAP_TEX_NODE], settings) + +def set_over_uv(node_tree, uv_layer): + """Set UV layer to texture in normal map flavor. + + :param node_tree: node tree on which normal map is used + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + # set uv layer to texture node + node_tree.nodes[OVER_NMAP_UVMAP_NODE].uv_map = uv_layer + + +def delete(node_tree, preserve_node=False): + """Delete over normal map nodes from node tree. + + :param node_tree: node tree from which normal map should be deleted + :type node_tree: bpy.types.NodeTree + :param preserve_node: if true node won't be deleted + :type preserve_node: bool + """ + + if OVER_NMAP_TEX_NODE in node_tree.nodes and not preserve_node: + node_tree.nodes.remove(node_tree.nodes[OVER_NMAP_UVMAP_NODE]) + node_tree.nodes.remove(node_tree.nodes[OVER_NMAP_TEX_NODE]) + node_tree.nodes.remove(node_tree.nodes[OVER_NMAP_COL_MIX_NODE]) + + nmap.delete(node_tree, preserve_node=preserve_node) diff --git a/addon/io_scs_tools/internals/shaders/eut2/dif_weight_dif/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/dif_weight_dif/__init__.py index 19b84e6f..6d95913b 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/dif_weight_dif/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/dif_weight_dif/__init__.py @@ -81,24 +81,25 @@ def init(node_tree): spec_mult_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1900) spec_mult_n.operation = "MULTIPLY" - base_over_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_over_mix_n = node_tree.nodes.new("ShaderNodeMix") base_over_mix_n.name = base_over_mix_n.label = DifWeightDif.BASE_OVER_MIX_NODE base_over_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1300) + base_over_mix_n.data_type = "RGBA" base_over_mix_n.blend_type = "MIX" # links creation node_tree.links.new(over_tex_n.inputs['Vector'], sec_uv_n.outputs['UV']) # pass 1 - node_tree.links.new(base_over_mix_n.inputs['Fac'], vcol_group_n.outputs['Vertex Color Alpha']) - node_tree.links.new(base_over_mix_n.inputs['Color1'], base_tex_n.outputs['Color']) - node_tree.links.new(base_over_mix_n.inputs['Color2'], over_tex_n.outputs['Color']) + node_tree.links.new(base_over_mix_n.inputs['Factor'], vcol_group_n.outputs['Vertex Color Alpha']) + node_tree.links.new(base_over_mix_n.inputs['A'], base_tex_n.outputs['Color']) + node_tree.links.new(base_over_mix_n.inputs['B'], over_tex_n.outputs['Color']) # pass 2 node_tree.links.new(spec_mult_n.inputs[0], spec_col_n.outputs[0]) node_tree.links.new(spec_mult_n.inputs[1], vcol_scale_n.outputs[0]) - node_tree.links.new(vcol_mult_n.inputs[1], base_over_mix_n.outputs['Color']) + node_tree.links.new(vcol_mult_n.inputs[1], base_over_mix_n.outputs['Result']) # pass to material node_tree.links.new(compose_lighting_n.inputs['Specular Color'], spec_mult_n.outputs[0]) @@ -115,12 +116,13 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] # set proper blend method if alpha_test.is_set(node_tree): - material.blend_method = "CLIP" - material.alpha_threshold = 0.05 + compose_lighting_n.inputs["Alpha Type"].default_value = 0.0 # add alpha test pass if multiply blend enabled, where alphed pixels shouldn't be multiplied as they are discarded if blend_mult.is_set(node_tree): @@ -137,13 +139,16 @@ def finalize(node_tree, material): alpha_test.add_pass(node_tree, shader_from, alpha_from, shader_to) if blend_add.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_mult.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_over.is_set(node_tree): - material.blend_method = "BLEND" + compose_lighting_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" - if material.blend_method == "OPAQUE" and node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: + if compose_lighting_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links[0]) @staticmethod @@ -222,7 +227,7 @@ def set_tg1_flavor(node_tree, switch_on): @staticmethod def set_aux1(node_tree, aux_property): - """Set second texture generation scale. + """Set second texture generation scale and rotation. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -231,5 +236,8 @@ def set_aux1(node_tree, aux_property): """ if tg1.is_set(node_tree): - - tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value']) + # Fix for old float2 aux[1] + if (len(aux_property)) == 2: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], 0) + else: + tg1.set_scale(node_tree, aux_property[0]['value'], aux_property[1]['value'], aux_property[2]['value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/fakeshadow/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/fakeshadow/__init__.py index 00498573..ee33445a 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/fakeshadow/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/fakeshadow/__init__.py @@ -18,12 +18,20 @@ # Copyright (C) 2015-2019: SCS Software +from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.base import BaseShader +from io_scs_tools.internals.shaders.flavors import nocull +from io_scs_tools.internals.shaders.flavors import alpha_test +from io_scs_tools.internals.shaders.std_node_groups import output_shader_ng +from io_scs_tools.utils import material as _material_utils class Fakeshadow(BaseShader): WIREFRAME_NODE = "Wireframe" MIX_NODE = "Mix" + UVMAP_NODE = "FirstUVs" + BASE_TEX_NODE = "BaseTex" + OUT_MAT_NODE = "OutMaterial" OUTPUT_NODE = "Output" @staticmethod @@ -47,24 +55,87 @@ def init(node_tree): # node creation wireframe_n = node_tree.nodes.new("ShaderNodeWireframe") wireframe_n.name = wireframe_n.label = Fakeshadow.WIREFRAME_NODE - wireframe_n.location = (start_pos_x, start_pos_y) + wireframe_n.location = (start_pos_x - pos_x_shift, start_pos_y) wireframe_n.use_pixel_size = True wireframe_n.inputs['Size'].default_value = 2.0 - mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") + uvmap_n.name = uvmap_n.label = Fakeshadow.UVMAP_NODE + uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y - 250) + uvmap_n.uv_map = _MESH_consts.none_uv + + mix_n = node_tree.nodes.new("ShaderNodeMix") mix_n.name = mix_n.label = Fakeshadow.MIX_NODE - mix_n.location = (start_pos_x + pos_x_shift, start_pos_y) - mix_n.inputs['Color1'].default_value = (1, 1, 1, 1) # fakeshadow color - mix_n.inputs['Color2'].default_value = (0, 0, 0, 1) # wireframe color + mix_n.location = (start_pos_x, start_pos_y) + mix_n.data_type = "RGBA" + mix_n.blend_type = "MIX" + mix_n.inputs['A'].default_value = (1, 1, 1, 1) # fakeshadow color + mix_n.inputs['B'].default_value = (0, 0, 0, 1) # wireframe color + + base_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + base_tex_n.name = base_tex_n.label = Fakeshadow.BASE_TEX_NODE + base_tex_n.location = (start_pos_x, start_pos_y - 250) + base_tex_n.width = 140 + + out_mat_node = node_tree.nodes.new("ShaderNodeGroup") + out_mat_node.name = out_mat_node.label = Fakeshadow.OUT_MAT_NODE + out_mat_node.location = (start_pos_x + pos_x_shift, start_pos_y - 100) + out_mat_node.node_tree = output_shader_ng.get_node_group() output_n = node_tree.nodes.new("ShaderNodeOutputMaterial") output_n.name = output_n.label = Fakeshadow.OUTPUT_NODE output_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y) # links creation - node_tree.links.new(mix_n.inputs['Fac'], wireframe_n.outputs['Fac']) + node_tree.links.new(mix_n.inputs['Factor'], wireframe_n.outputs['Fac']) + node_tree.links.new(output_n.inputs['Surface'], mix_n.outputs['Result']) + node_tree.links.new(out_mat_node.inputs['Color'], mix_n.outputs['Result']) + + node_tree.links.new(base_tex_n.inputs['Vector'], uvmap_n.outputs['UV']) + node_tree.links.new(out_mat_node.inputs['Alpha'], base_tex_n.outputs['Alpha']) + + @staticmethod + def finalize(node_tree, material): + """Finalize node tree and material settings. Should be called as last. + + :param node_tree: node tree on which this shader should be finalized + :type node_tree: bpy.types.NodeTree + :param material: material used for this shader + :type material: bpy.types.Material + """ + + material.use_backface_culling = True + material.surface_render_method = "DITHERED" + + if nocull.is_set(node_tree): + material.use_backface_culling = False + + # set proper blend method and possible alpha test pass + if alpha_test.is_set(node_tree): + + # init parent + out_mat_node = node_tree.nodes[Fakeshadow.OUT_MAT_NODE] + output_n = node_tree.nodes[Fakeshadow.OUTPUT_NODE] - node_tree.links.new(output_n.inputs['Surface'], mix_n.outputs['Color']) + out_mat_node.inputs["Alpha Type"].default_value = 0.0 + + # links creation + node_tree.links.new(out_mat_node.outputs['Shader'], output_n.inputs['Surface']) + + @staticmethod + def set_alpha_test_flavor(node_tree, switch_on): + """Set alpha test flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if alpha test should be switched on or off + :type switch_on: bool + """ + + if switch_on: + alpha_test.init(node_tree) + else: + alpha_test.delete(node_tree) @staticmethod def set_shadow_bias(node_tree, value): @@ -79,14 +150,55 @@ def set_shadow_bias(node_tree, value): pass # NOTE: shadow bias won't be visualized as game uses it's own implementation @staticmethod - def finalize(node_tree, material): - """Finalize node tree and material settings. Should be called as last. + def set_nocull_flavor(node_tree, switch_on): + """Set nocull flavor to this shader. - :param node_tree: node tree on which this shader should be finalized + :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree - :param material: material used for this shader - :type material: bpy.types.Material + :param switch_on: flag indication if it should be switched on or off + :type switch_on: bool """ - material.use_backface_culling = True - material.blend_method = "OPAQUE" + if switch_on: + nocull.init(node_tree) + else: + nocull.delete(node_tree) + + @staticmethod + def set_base_texture(node_tree, image): + """Set base texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to base texture node + :type image: bpy.types.Image + """ + if alpha_test.is_set(node_tree): + node_tree.nodes[Fakeshadow.BASE_TEX_NODE].image = image + + @staticmethod + def set_base_texture_settings(node_tree, settings): + """Set base texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + if alpha_test.is_set(node_tree): + _material_utils.set_texture_settings_to_node(node_tree.nodes[Fakeshadow.BASE_TEX_NODE], settings) + + @staticmethod + def set_base_uv(node_tree, uv_layer): + """Set UV layer to base texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base texture + :type uv_layer: str + """ + if alpha_test.is_set(node_tree): + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[Fakeshadow.UVMAP_NODE].uv_map = uv_layer diff --git a/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py index 214f48a2..19055e38 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/glass/__init__.py @@ -27,6 +27,7 @@ from io_scs_tools.internals.shaders.eut2.std_node_groups import lighting_evaluator_ng from io_scs_tools.internals.shaders.eut2.std_node_groups import refl_normal_ng from io_scs_tools.internals.shaders.eut2.std_node_groups import vcolor_input_ng +from io_scs_tools.internals.shaders.flavors import nmap from io_scs_tools.utils import convert as _convert_utils from io_scs_tools.utils import material as _material_utils @@ -204,9 +205,10 @@ def init(node_tree): lighting_eval_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 1800) lighting_eval_n.node_tree = lighting_evaluator_ng.get_node_group() - fakeopac_hsv_n = node_tree.nodes.new("ShaderNodeSeparateHSV") + fakeopac_hsv_n = node_tree.nodes.new("ShaderNodeSeparateColor") fakeopac_hsv_n.name = fakeopac_hsv_n.label = Glass.FAKEOPAC_HSV_NODE fakeopac_hsv_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 1500) + fakeopac_hsv_n.mode = "HSV" # pass 6 add_env_n = node_tree.nodes.new("ShaderNodeGroup") @@ -324,14 +326,14 @@ def init(node_tree): node_tree.links.new(fakeopac_spec_mix_n.inputs[0], final_spec_n.outputs[0]) node_tree.links.new(fakeopac_spec_mix_n.inputs[1], lighting_eval_n.outputs['Specular Lighting']) - node_tree.links.new(fakeopac_add_sv_n.inputs[0], fakeopac_hsv_n.outputs['S']) - node_tree.links.new(fakeopac_add_sv_n.inputs[1], fakeopac_hsv_n.outputs['V']) + node_tree.links.new(fakeopac_add_sv_n.inputs[0], fakeopac_hsv_n.outputs[1]) # Saturation + node_tree.links.new(fakeopac_add_sv_n.inputs[1], fakeopac_hsv_n.outputs[2]) # Value # pass 7 node_tree.links.new(fakeopac_sub_sv_n.inputs[0], fakeopac_add_sv_n.outputs['Value']) - node_tree.links.new(fakeopac_sub_sv_n.inputs[1], fakeopac_hsv_n.outputs['V']) + node_tree.links.new(fakeopac_sub_sv_n.inputs[1], fakeopac_hsv_n.outputs[2]) # Value - node_tree.links.new(fakeopac_v_inv_n.inputs[1], fakeopac_hsv_n.outputs['V']) + node_tree.links.new(fakeopac_v_inv_n.inputs[1], fakeopac_hsv_n.outputs[2]) # Value # pass 8 node_tree.links.new(fakeopac_add_spec_mix_n.inputs[0], add_env_n.outputs['Environment Addition Color']) @@ -370,7 +372,8 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "BLEND" + material.surface_render_method = "BLENDED" + node_tree.nodes[Glass.COMPOSE_LIGHTING_NODE].inputs["Alpha Type"].default_value = 1.0 @staticmethod def set_add_ambient(node_tree, factor): @@ -551,3 +554,64 @@ def set_tint(node_tree, color): color = _convert_utils.to_node_color(color) node_tree.nodes[Glass.TINT_COL_NODE].outputs[0].default_value = color + + @staticmethod + def set_nmap_flavor(node_tree, switch_on): + """Set normal map flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if normal map should be switched on or off + :type switch_on: bool + """ + + if switch_on: + + # find minimal y position for input nodes and position flavor beneath it + min_y = None + for node in node_tree.nodes: + if node.location.x <= 185 and (min_y is None or min_y > node.location.y): + min_y = node.location.y + + lighting_eval_n = node_tree.nodes[Glass.LIGHTING_EVAL_NODE] + geom_n = node_tree.nodes[Glass.GEOM_NODE] + location = (lighting_eval_n.location.x - 185, min_y - 400) + + nmap.init(node_tree, location, lighting_eval_n.inputs['Normal Vector'], geom_n.outputs['Normal']) + else: + nmap.delete(node_tree) + + @staticmethod + def set_nmap_texture(node_tree, image): + """Set normal map texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to nmap texture node + :type image: bpy.types.Image + """ + + nmap.set_texture(node_tree, image) + + @staticmethod + def set_nmap_texture_settings(node_tree, settings): + """Set normal map texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + nmap.set_texture_settings(node_tree, settings) + + @staticmethod + def set_nmap_uv(node_tree, uv_layer): + """Set UV layer to normal map texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for nmap texture + :type uv_layer: str + """ + + nmap.set_uv(node_tree, uv_layer) \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/interior/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/interior/__init__.py new file mode 100644 index 00000000..893c4eb4 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/interior/__init__.py @@ -0,0 +1,286 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2024: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.parameters import get_fresnel_window +from io_scs_tools.internals.shaders.eut2.dif_spec_add_env import DifSpecAddEnv +from io_scs_tools.utils import material as _material_utils + +class InteriorLit(DifSpecAddEnv): + VGCOLOR_MULT_NODE = "VertexGlassColorMultiplier" + GLASS_COL_NODE = "GlassColor" + GLASS_COL_MIX_NODE = "GlassColorMix" + LAYER0_TEX_NODE = "Layer0Tex" + LAYER1_TEX_NODE = "Layer1Tex" + MASK_TEX_NODE = "MaskTex" + NMAP_TEX_NODE = "NmapTex" + ENV_SEP_XYZ_NODE = "EnvSepXYZ" + ENV_CHECK_XYZ_NODE = "EnvCheckXYZ" + PERT_UVMAP_NODE = "PerturbationUVMap" + + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + DifSpecAddEnv.init(node_tree) + + base_tex_n = node_tree.nodes[DifSpecAddEnv.BASE_TEX_NODE] + uvmap_n = node_tree.nodes[DifSpecAddEnv.UVMAP_NODE] + vcol_mult_n = node_tree.nodes[DifSpecAddEnv.VCOLOR_MULT_NODE] + diff_mult_n = node_tree.nodes[DifSpecAddEnv.DIFF_MULT_NODE] + add_env_group_n = node_tree.nodes[DifSpecAddEnv.ADD_ENV_GROUP_NODE] + + # set fresnel type to schlick + add_env_group_n.inputs['Fresnel Type'].default_value = 1.0 + + # delete existing + node_tree.nodes.remove(node_tree.nodes[DifSpecAddEnv.OPACITY_NODE]) + + # move existing + diff_mult_n.location.x += pos_x_shift + vcol_mult_n.location.x += pos_x_shift + + # node creation + # - column -1 - + pert_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + pert_uv_n.name = pert_uv_n.label = InteriorLit.PERT_UVMAP_NODE + pert_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 950) + pert_uv_n.uv_map = _MESH_consts.none_uv + + # - column 0 - + env_sep_xyz_n = node_tree.nodes.new("ShaderNodeSeparateXYZ") + env_sep_xyz_n.name = env_sep_xyz_n.label = InteriorLit.ENV_SEP_XYZ_NODE + env_sep_xyz_n.location = (start_pos_x, start_pos_y + 1800) + + env_check_xyz_n = node_tree.nodes.new("ShaderNodeMath") + env_check_xyz_n.name = env_check_xyz_n.label = InteriorLit.ENV_CHECK_XYZ_NODE + env_check_xyz_n.location = (start_pos_x, start_pos_y + 2000) + env_check_xyz_n.operation = "LESS_THAN" + env_check_xyz_n.inputs[1].default_value = 1.0 + + # - column 1 - + glass_col_n = node_tree.nodes.new("ShaderNodeRGB") + glass_col_n.name = glass_col_n.label = InteriorLit.GLASS_COL_NODE + glass_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 900) + + """ + layer0_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + layer0_tex_n.name = layer0_tex_n.label = InteriorLit.LAYER0_TEX_NODE + layer0_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 700) + layer0_tex_n.width = 140 + + layer1_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + layer1_tex_n.name = layer1_tex_n.label = InteriorLit.LAYER1_TEX_NODE + layer1_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 450) + layer1_tex_n.width = 140 + + mask_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_tex_n.name = mask_tex_n.label = InteriorLit.MASK_TEX_NODE + mask_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 200) + mask_tex_n.width = 140 + + nmap_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + nmap_tex_n.name = nmap_tex_n.label = InteriorLit.NMAP_TEX_NODE + nmap_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y - 50) + nmap_tex_n.width = 140 + """ + + # - column 3 - + vgcol_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + vgcol_mult_n.name = vgcol_mult_n.label = InteriorLit.VGCOLOR_MULT_NODE + vgcol_mult_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1200) + vgcol_mult_n.operation = "MULTIPLY" + + # - column 4 - + glass_col_mix_n = node_tree.nodes.new("ShaderNodeMix") + glass_col_mix_n.name = glass_col_mix_n.label = InteriorLit.GLASS_COL_MIX_NODE + glass_col_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1200) + glass_col_mix_n.data_type = "RGBA" + glass_col_mix_n.blend_type = "MIX" + + + # links creation + # - column -1 - + node_tree.links.new(uvmap_n.outputs['UV'], env_sep_xyz_n.inputs['Vector']) + + # - column 0 - + node_tree.links.new(env_sep_xyz_n.outputs['Y'], env_check_xyz_n.inputs[0]) + node_tree.links.new(env_check_xyz_n.outputs['Value'], add_env_group_n.inputs['Weighted Color']) + + # - column 1 - + node_tree.links.new(base_tex_n.outputs['Color'], vgcol_mult_n.inputs[0]) + + node_tree.links.new(glass_col_n.outputs['Color'], vgcol_mult_n.inputs[1]) + node_tree.links.new(glass_col_n.outputs['Color'], glass_col_mix_n.inputs['B']) + + # - column 3 - + node_tree.links.new(vgcol_mult_n.outputs['Vector'], glass_col_mix_n.inputs['A']) + + # - column 4 - + node_tree.links.new(glass_col_mix_n.outputs['Result'], vcol_mult_n.inputs[1]) + + ### TEXTURES ### + @staticmethod + def set_nmap_texture(node_tree, image): + """Set nmap texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to nmap texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[InteriorLit.NMAP_TEX_NODE].image = image + + @staticmethod + def set_nmap_texture_settings(node_tree, settings): + """Set nmap texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[InteriorLit.NMAP_TEX_NODE], settings) + + ### ATTRIBUTES ### + @staticmethod + def set_fresnel(node_tree, bias_scale): + """Set fresnel bias and scale value to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param bias_scale: bias and scale factors as tuple: (bias, scale) + :type bias_scale: (float, float) + """ + + bias_scale_window = get_fresnel_window(bias_scale[0], bias_scale[1]) + + DifSpecAddEnv.set_fresnel(node_tree, bias_scale_window) + + @staticmethod + def set_aux0(node_tree, aux_property): + """Set unit room dimension for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: TBA + :type aux_property: bpy.types.IDPropertyGroup + """ + # NOTE: TBA? + pass # NOTE: aux doesn't change anything in rendered material, so pass it + + @staticmethod + def set_aux1(node_tree, aux_property): + """Set atlas dimension for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: TBA + :type aux_property: bpy.types.IDPropertyGroup + """ + # NOTE: TBA? + pass # NOTE: aux doesn't change anything in rendered material, so pass it + + @staticmethod + def set_aux2(node_tree, aux_property): + """Set glass color for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: glass color + :type aux_property: bpy.types.IDPropertyGroup + """ + + color = (aux_property[0]["value"], aux_property[1]["value"], aux_property[2]["value"], 1.0) + node_tree.nodes[InteriorLit.GLASS_COL_NODE].outputs["Color"].default_value = color + + factor = aux_property[3]["value"] + + + node_tree.nodes[InteriorLit.GLASS_COL_MIX_NODE].inputs['Factor'].default_value = factor + + @staticmethod + def set_aux5(node_tree, aux_property): + """Set luminance boost factor for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: luminosity factor represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + pass # NOTE: as this variant doesn't use luminance effect we just ignore this factor + + + # def made to override base texture settings, because overwriting "extension" in init doesn't work + @staticmethod + def set_base_texture_settings(node_tree, settings): + """Set base texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + + node_tree.nodes[DifSpecAddEnv.BASE_TEX_NODE].extension = 'REPEAT' + + @staticmethod + def set_perturbation_mapping(node_tree, uv_layer): + """Set Perturbation UV layer to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[InteriorLit.PERT_UVMAP_NODE].uv_map = uv_layer + + @staticmethod + def set_lit_flavor(node_tree, switch_on): + """Set lit for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if flavor should be switched on or off + :type switch_on: bool + """ + + pass diff --git a/addon/io_scs_tools/internals/shaders/eut2/interior/curtain.py b/addon/io_scs_tools/internals/shaders/eut2/interior/curtain.py new file mode 100644 index 00000000..1ec8a56e --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/interior/curtain.py @@ -0,0 +1,124 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2015-2024: SCS Software + +from io_scs_tools.consts import Mesh as _MESH_consts +from io_scs_tools.internals.shaders.eut2.interior import InteriorLit +from io_scs_tools.utils import material as _material_utils + +class InteriorCurtain(InteriorLit): + SEC_UVMAP_NODE = "SecondUVMap" + OVER_TEX_NODE = "OverTex" + BASE_OVER_MIX_NODE = "BaseOverColorMix" + + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + InteriorLit.init(node_tree) + + base_tex_n = node_tree.nodes[InteriorLit.BASE_TEX_NODE] + vgcol_mult_n = node_tree.nodes[InteriorLit.VGCOLOR_MULT_NODE] + + # node creation + # - column -1 - + sec_uv_n = node_tree.nodes.new("ShaderNodeUVMap") + sec_uv_n.name = sec_uv_n.label = InteriorCurtain.SEC_UVMAP_NODE + sec_uv_n.location = (start_pos_x - pos_x_shift, start_pos_y + 1100) + sec_uv_n.uv_map = _MESH_consts.none_uv + + # - column 1 - + over_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + over_tex_n.name = over_tex_n.label = InteriorCurtain.OVER_TEX_NODE + over_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) + over_tex_n.width = 140 + + # - column 2 - + base_over_mix_n = node_tree.nodes.new("ShaderNodeMix") + base_over_mix_n.name = base_over_mix_n.label = InteriorCurtain.BASE_OVER_MIX_NODE + base_over_mix_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 1200) + base_over_mix_n.data_type = "RGBA" + base_over_mix_n.blend_type = "MIX" + + + # links creation + # - column -1 - + node_tree.links.new(sec_uv_n.outputs['UV'], over_tex_n.inputs['Vector']) + + # - column 1 - + node_tree.links.new(base_tex_n.outputs['Color'], base_over_mix_n.inputs['A']) + node_tree.links.new(over_tex_n.outputs['Color'], base_over_mix_n.inputs['B']) + node_tree.links.new(over_tex_n.outputs['Alpha'], base_over_mix_n.inputs['Factor']) + + # - column 2 - + node_tree.links.new(base_over_mix_n.outputs['Result'], vgcol_mult_n.inputs[0]) + + @staticmethod + def set_over_texture(node_tree, image): + """Set overlying texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to over texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[InteriorCurtain.OVER_TEX_NODE].image = image + + @staticmethod + def set_over_texture_settings(node_tree, settings): + """Set overlying texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[InteriorCurtain.OVER_TEX_NODE], settings) + + @staticmethod + def set_over_uv(node_tree, uv_layer): + """Set UV layer to overlying texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over texture + :type uv_layer: str + """ + + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[InteriorCurtain.SEC_UVMAP_NODE].uv_map = uv_layer diff --git a/addon/io_scs_tools/internals/shaders/eut2/leaves/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/leaves/__init__.py new file mode 100644 index 00000000..ff4851dd --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/leaves/__init__.py @@ -0,0 +1,172 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.internals.shaders.eut2.dif import Dif +from io_scs_tools.utils import material as _material_utils + +class Leaves(Dif): + MASK_TEX_NODE = "MaskTex" + SPEC_MULT_NODE = "SpecMultiplier" + SEP_MASK_COL_NODE = "SeparateMaskCol" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + DISCLAIMER: This shader is provisional and should be reworked as soon, as more information about leaves shader will be available. + Please, do not treat that preview as real equivalent of game shader. Please, chceck everything in game after export. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + Dif.init(node_tree) + + uvmap_n = node_tree.nodes[Dif.UVMAP_NODE] + base_tex_n = node_tree.nodes[Dif.BASE_TEX_NODE] + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] + spec_col_n = node_tree.nodes[Dif.SPEC_COL_NODE] + diff_mult_n = node_tree.nodes[Dif.DIFF_MULT_NODE] + + opacity_mult_n = node_tree.nodes[Dif.OPACITY_NODE] + vcol_scale_n = node_tree.nodes[Dif.VCOLOR_SCALE_NODE] + vcol_mult_n = node_tree.nodes[Dif.VCOLOR_MULT_NODE] + + # delete existing + node_tree.nodes.remove(opacity_mult_n) + node_tree.nodes.remove(vcol_scale_n) + node_tree.nodes.remove(vcol_mult_n) + + # node creation + # - column 1 - + mask_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_tex_n.name = mask_tex_n.label = Leaves.MASK_TEX_NODE + mask_tex_n.location = (start_pos_x + pos_x_shift, start_pos_y + 1200) + mask_tex_n.width = 140 + + # - column 2 - + sep_mask_col_n = node_tree.nodes.new("ShaderNodeSeparateColor") + sep_mask_col_n.name = sep_mask_col_n.label = Leaves.SEP_MASK_COL_NODE + sep_mask_col_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 1200) + + # - column 3 - + spec_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + spec_mult_n.name = spec_mult_n.label = Leaves.SPEC_MULT_NODE + spec_mult_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1900) + spec_mult_n.operation = "MULTIPLY" + + # links creation + # - column -1 - + node_tree.links.new(uvmap_n.outputs['UV'], mask_tex_n.inputs['Vector']) + + # - column 1 - + node_tree.links.new(spec_col_n.outputs['Color'], spec_mult_n.inputs[0]) + + node_tree.links.new(base_tex_n.outputs['Color'], diff_mult_n.inputs[1]) + node_tree.links.new(base_tex_n.outputs['Alpha'], compose_lighting_n.inputs['Alpha']) + + node_tree.links.new(mask_tex_n.outputs['Color'], sep_mask_col_n.inputs['Color']) + + # - column 2 - + # Idk if this is correct. This is made to prevent specular shading on model. It can also be done by "Flat Light" in light evaluator, + # but I think, that B channel is used for specular mask because that channel is very similar to specular effect on leaves in game. + # It also can be G or anything else. I don't have time to test that shader in game. + node_tree.links.new(sep_mask_col_n.outputs['Blue'], spec_mult_n.inputs[1]) + + # - column 3 - + node_tree.links.new(spec_mult_n.outputs['Vector'], compose_lighting_n.inputs['Specular Color']) + + @staticmethod + def finalize(node_tree, material): + """Finalize node tree and material settings. Should be called as last. + + :param node_tree: node tree on which this shader should be finalized + :type node_tree: bpy.types.NodeTree + :param material: material used for this shader + :type material: bpy.types.Material + """ + + compose_lighting_n = node_tree.nodes[Leaves.COMPOSE_LIGHTING_NODE] + + material.use_backface_culling = False + material.surface_render_method = "DITHERED" + compose_lighting_n.inputs["Alpha Type"].default_value = 0.0 + + if compose_lighting_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[Leaves.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links: + node_tree.links.remove(node_tree.nodes[Leaves.COMPOSE_LIGHTING_NODE].inputs['Alpha'].links[0]) + + @staticmethod + def set_mask_texture(node_tree, image): + """Set mask texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to mask texture node + :type image: bpy.types.Texture + """ + + node_tree.nodes[Leaves.MASK_TEX_NODE].image = image + + @staticmethod + def set_mask_texture_settings(node_tree, settings): + """Set mask texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Leaves.MASK_TEX_NODE], settings) + + @staticmethod + def set_mask_uv(node_tree, uv_layer): + """Set UV layer to mask texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for mask texture + :type uv_layer: str + """ + + Dif.set_base_uv(node_tree, uv_layer) + + @staticmethod + def set_aux1(node_tree, aux_property): + """Set LOD selector for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: LOD selector represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + # NOTE: As long as we not use it for now, we can pass it. + pass \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/light/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/light/__init__.py new file mode 100644 index 00000000..9389d7fe --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/light/__init__.py @@ -0,0 +1,116 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.internals.shaders.eut2.dif import Dif + + +class Light(Dif): + SPEC_MULT_NODE = "SpecMultiplier" + RGB_TO_BW_ALPHA_NODE = "RGBToBWColor" + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + start_pos_x = 0 + start_pos_y = 0 + + pos_x_shift = 185 + + # init parent + Dif.init(node_tree) + + # delete existing + node_tree.nodes.remove(node_tree.nodes[Dif.OPACITY_NODE]) + node_tree.nodes.remove(node_tree.nodes[Dif.LIGHTING_EVAL_NODE]) + node_tree.nodes.remove(node_tree.nodes[Dif.BASE_TEX_NODE]) + node_tree.nodes.remove(node_tree.nodes[Dif.UVMAP_NODE]) + + vcol_group_n = node_tree.nodes[Dif.VCOL_GROUP_NODE] + v_col_mult_n = node_tree.nodes[Dif.VCOLOR_MULT_NODE] + v_col_mult_n.inputs[1].default_value = (1.0,) *3 + compose_lighting_n = node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE] + compose_lighting_n.inputs['Diffuse Lighting'].default_value = (1.0,) * 4 + compose_lighting_n.inputs['Specular Lighting'].default_value = (1.0,) * 4 + + # node creation + spec_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") + spec_mult_n.name = Light.SPEC_MULT_NODE + spec_mult_n.label = Light.SPEC_MULT_NODE + spec_mult_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 1850) + spec_mult_n.operation = "MULTIPLY" + + rgb_to_bw_n = node_tree.nodes.new("ShaderNodeRGBToBW") + rgb_to_bw_n.name = Light.RGB_TO_BW_ALPHA_NODE + rgb_to_bw_n.label = Light.RGB_TO_BW_ALPHA_NODE + rgb_to_bw_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 1300) + + # links creation + node_tree.links.new(spec_mult_n.inputs[0], v_col_mult_n.outputs[0]) + node_tree.links.new(compose_lighting_n.inputs["Specular Color"], spec_mult_n.outputs[0]) + + node_tree.links.new(rgb_to_bw_n.inputs["Color"], vcol_group_n.outputs["Vertex Color"]) + node_tree.links.new(compose_lighting_n.inputs["Alpha"], rgb_to_bw_n.outputs["Val"]) + + @staticmethod + def finalize(node_tree, material): + """Finalize node tree and material settings. Should be called as last. + + :param node_tree: node tree on which this shader should be finalized + :type node_tree: bpy.types.NodeTree + :param material: material used for this shader + :type material: bpy.types.Material + """ + + # in game it gets added to framebuffer, however we don't have access to frame buffer thus make approximation with alpha blending + material.surface_render_method = "BLENDED" + node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs["Alpha Type"].default_value = 1.0 + + @staticmethod + def set_env_factor(node_tree, color): + """Set environment factor color to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param color: environment color + :type color: Color or tuple + """ + + pass # NOTE: as this variant doesn't use env_factor we just ignore it + + @staticmethod + def set_aux5(node_tree, aux_property): + """Set luminance boost factor for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: luminosity factor represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + pass # NOTE: as this variant doesn't use luminance effect we just ignore this factor diff --git a/addon/io_scs_tools/internals/shaders/eut2/light_tex/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/light_tex/__init__.py index e92045a0..bac6ac4a 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/light_tex/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/light_tex/__init__.py @@ -84,10 +84,12 @@ def finalize(node_tree, material): :param material: material used for this shader :type material: bpy.types.Material """ - Dif.finalize(node_tree, material) + # Cause problems with proper links creation ("rgb_to_bw_n" to "compose_lighting_n") + # Dif.finalize(node_tree, material) # in game it gets added to framebuffer, however we don't have access to frame buffer thus make approximation with alpha blending - material.blend_method = "BLEND" + material.surface_render_method = "BLENDED" + node_tree.nodes[Dif.COMPOSE_LIGHTING_NODE].inputs["Alpha Type"].default_value = 1.0 @staticmethod def set_shininess(node_tree, factor): @@ -149,6 +151,18 @@ def set_blend_mult_flavor(node_tree, switch_on): pass # NOTE: no support for this flavor; overriding with empty function + @staticmethod + def set_env_factor(node_tree, color): + """Set environment factor color to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param color: environment color + :type color: Color or tuple + """ + + pass # NOTE: as this variant doesn't use env_factor we just ignore it + @staticmethod def set_aux0(node_tree, aux_property): """Set depth bias. @@ -160,3 +174,14 @@ def set_aux0(node_tree, aux_property): """ pass # NOTE: no support for this parameter as there is no way to simulate eye-space z-bias + + @staticmethod + def set_aux5(node_tree, aux_property): + """Set luminance boost factor for the shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: luminosity factor represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + pass # NOTE: as this variant doesn't use luminance effect we just ignore this factor diff --git a/addon/io_scs_tools/internals/shaders/eut2/none/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/none/__init__.py index 88f41a73..9b06b2b4 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/none/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/none/__init__.py @@ -74,4 +74,5 @@ def finalize(node_tree, material): """ # make sure that alpha clip is enabled on that material - material.blend_method = "CLIP" + material.surface_render_method = "DITHERED" + node_tree.nodes[NNone.SHADER_NODE].inputs["Alpha Type"].default_value = 0.0 diff --git a/addon/io_scs_tools/internals/shaders/eut2/particle/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/particle/__init__.py new file mode 100644 index 00000000..8519d495 --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/particle/__init__.py @@ -0,0 +1,52 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +from io_scs_tools.internals.shaders.eut2.dif import Dif + + +class Particle(Dif): + + @staticmethod + def get_name(): + """Get name of this shader file with full modules path.""" + return __name__ + + @staticmethod + def init(node_tree): + """Initialize node tree with links for this shader. + + :param node_tree: node tree on which this shader should be created + :type node_tree: bpy.types.NodeTree + """ + + # init parent + Dif.init(node_tree) + + @staticmethod + def set_aux5(node_tree, aux_property): + """Set luminosity boost factor. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param aux_property: luminosity output represented with property group + :type aux_property: bpy.types.IDPropertyGroup + """ + + pass # NOTE: as this variant doesn't use luminance effect we just ignore this factor diff --git a/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py index 7474f0d2..d386f36f 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/reflective/__init__.py @@ -137,7 +137,8 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "BLEND" + material.surface_render_method = "BLENDED" + node_tree.nodes[Reflective.COMPOSE_LIGHTING_NODE].inputs["Alpha Type"].default_value = 1.0 @staticmethod def set_base_texture(node_tree, image): diff --git a/addon/io_scs_tools/internals/shaders/eut2/shadowmap/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/shadowmap/__init__.py index 59887279..1f7c59b8 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/shadowmap/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/shadowmap/__init__.py @@ -87,7 +87,8 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "BLEND" + material.surface_render_method = "BLENDED" + node_tree.nodes[Shadowmap.OUT_SHADER_NODE].inputs["Alpha Type"].default_value = 1.0 @staticmethod def set_base_texture(node_tree, image): diff --git a/addon/io_scs_tools/internals/shaders/eut2/shadowonly/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/shadowonly/__init__.py index 3a1799a4..a19349c6 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/shadowonly/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/shadowonly/__init__.py @@ -18,12 +18,20 @@ # Copyright (C) 2015-2019: SCS Software +from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.base import BaseShader +from io_scs_tools.internals.shaders.flavors import nocull +from io_scs_tools.internals.shaders.flavors import alpha_test +from io_scs_tools.internals.shaders.std_node_groups import output_shader_ng +from io_scs_tools.utils import material as _material_utils class Shadowonly(BaseShader): COL_NODE = "Color" OUTPUT_NODE = "Output" + UVMAP_NODE = "FirstUVs" + BASE_TEX_NODE = "BaseTex" + OUT_MAT_NODE = "OutMaterial" @staticmethod def get_name(): @@ -44,18 +52,37 @@ def init(node_tree): pos_x_shift = 185 # node creation + uvmap_n = node_tree.nodes.new("ShaderNodeUVMap") + uvmap_n.name = uvmap_n.label = Shadowonly.UVMAP_NODE + uvmap_n.location = (start_pos_x - pos_x_shift, start_pos_y - 200) + uvmap_n.uv_map = _MESH_consts.none_uv + col_n = node_tree.nodes.new("ShaderNodeRGB") col_n.name = col_n.label = Shadowonly.COL_NODE col_n.location = (start_pos_x, start_pos_y) col_n.outputs['Color'].default_value = (0.01, 0, 0.01, 1.0) + base_tex_n = node_tree.nodes.new("ShaderNodeTexImage") + base_tex_n.name = base_tex_n.label = Shadowonly.BASE_TEX_NODE + base_tex_n.location = (start_pos_x, start_pos_y - 200) + base_tex_n.width = 140 + + out_mat_node = node_tree.nodes.new("ShaderNodeGroup") + out_mat_node.name = out_mat_node.label = Shadowonly.OUT_MAT_NODE + out_mat_node.location = (start_pos_x + pos_x_shift, start_pos_y - 100) + out_mat_node.node_tree = output_shader_ng.get_node_group() + output_n = node_tree.nodes.new("ShaderNodeOutputMaterial") output_n.name = output_n.label = Shadowonly.OUTPUT_NODE - output_n.location = (start_pos_x + pos_x_shift, start_pos_y) + output_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y) # links creation node_tree.links.new(output_n.inputs['Surface'], col_n.outputs['Color']) + node_tree.links.new(uvmap_n.outputs['UV'], base_tex_n.inputs['Vector']) + node_tree.links.new(col_n.outputs['Color'], out_mat_node.inputs['Color']) + node_tree.links.new(base_tex_n.outputs['Alpha'], out_mat_node.inputs['Alpha']) + @staticmethod def finalize(node_tree, material): """Finalize node tree and material settings. Should be called as last. @@ -67,7 +94,91 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + if nocull.is_set(node_tree): + material.use_backface_culling = False + + # set proper blend method and possible alpha test pass + if alpha_test.is_set(node_tree): + + # init parent + out_mat_node = node_tree.nodes[Shadowonly.OUT_MAT_NODE] + output_n = node_tree.nodes[Shadowonly.OUTPUT_NODE] + + out_mat_node.inputs["Alpha Type"].default_value = 0.0 + + # links creation + node_tree.links.new(out_mat_node.outputs['Shader'], output_n.inputs['Surface']) + + @staticmethod + def set_alpha_test_flavor(node_tree, switch_on): + """Set alpha test flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if alpha test should be switched on or off + :type switch_on: bool + """ + + if switch_on: + alpha_test.init(node_tree) + else: + alpha_test.delete(node_tree) + + @staticmethod + def set_nocull_flavor(node_tree, switch_on): + """Set nocull flavor to this shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param switch_on: flag indication if it should be switched on or off + :type switch_on: bool + """ + + if switch_on: + nocull.init(node_tree) + else: + nocull.delete(node_tree) + + @staticmethod + def set_base_texture(node_tree, image): + """Set base texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assignet to base texture node + :type image: bpy.types.Image + """ + if alpha_test.is_set(node_tree): + node_tree.nodes[Shadowonly.BASE_TEX_NODE].image = image + + @staticmethod + def set_base_texture_settings(node_tree, settings): + """Set base texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + if alpha_test.is_set(node_tree): + _material_utils.set_texture_settings_to_node(node_tree.nodes[Shadowonly.BASE_TEX_NODE], settings) + + @staticmethod + def set_base_uv(node_tree, uv_layer): + """Set UV layer to base texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base texture + :type uv_layer: str + """ + if alpha_test.is_set(node_tree): + if uv_layer is None or uv_layer == "": + uv_layer = _MESH_consts.none_uv + + node_tree.nodes[Shadowonly.UVMAP_NODE].uv_map = uv_layer @staticmethod def set_shadow_bias(node_tree, value): diff --git a/addon/io_scs_tools/internals/shaders/eut2/sky/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/sky/__init__.py index 8b573863..a8ac4b93 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/sky/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/sky/__init__.py @@ -18,10 +18,9 @@ # Copyright (C) 2015-2022: SCS Software -from mathutils import Color from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.internals.shaders.base import BaseShader -from io_scs_tools.internals.shaders.flavors import sky_back, sky_stars +from io_scs_tools.internals.shaders.flavors import sky_bottom, sky_stars from io_scs_tools.internals.shaders.eut2.std_node_groups import vcolor_input_ng from io_scs_tools.internals.shaders.eut2.sky import texture_types from io_scs_tools.internals.shaders.eut2.sky import uv_rescale_ng @@ -52,6 +51,7 @@ class Sky(BaseShader): SEPARATE_UV_NODE_PREFIX = "Separate UV " TEX_NODE_PREFIX = "Tex " + MASK_NODE_PREFIX = "Mask " TEX_OOB_BOOL_NODE_PREFIX = "OOBBool " TEX_FINAL_MIX_NODE_PREFIX = "FinalTexel " TEX_FINAL_A_MIX_NODE_PREFIX = "FinalTexelA " @@ -96,7 +96,7 @@ def init(node_tree): diff_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 2000) for tex_type_i, tex_type in enumerate(texture_types.get()): - Sky.__create_texel_component_nodes__(node_tree, tex_type, (start_pos_x + pos_x_shift, start_pos_y + 1500 - tex_type_i * 450)) + Sky.__create_texel_component_nodes__(node_tree, tex_type, (start_pos_x + pos_x_shift, start_pos_y + 1500 - tex_type_i * 750)) base_a_tex_final_n = node_tree.nodes[Sky.TEX_FINAL_MIX_NODE_PREFIX + texture_types.get()[0]] base_b_tex_final_n = node_tree.nodes[Sky.TEX_FINAL_MIX_NODE_PREFIX + texture_types.get()[1]] @@ -119,34 +119,40 @@ def init(node_tree): diff_mult_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 2000) diff_mult_n.operation = "MULTIPLY" - weather_base_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + weather_base_mix_n = node_tree.nodes.new("ShaderNodeMix") weather_base_mix_n.name = weather_base_mix_n.label = Sky.WEATHER_BASE_MIX_NODE - weather_base_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1500) + weather_base_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1250) # +1400 + weather_base_mix_n.data_type = "RGBA" weather_base_mix_n.blend_type = "MIX" - weather_base_alpha_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + weather_base_alpha_mix_n = node_tree.nodes.new("ShaderNodeMix") weather_base_alpha_mix_n.name = weather_base_alpha_mix_n.label = Sky.WEATHER_BASE_A_MIX_NODE - weather_base_alpha_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1300) + weather_base_alpha_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1000) # +1150 + weather_base_alpha_mix_n.data_type = "RGBA" weather_base_alpha_mix_n.blend_type = "MIX" - weather_over_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + weather_over_mix_n = node_tree.nodes.new("ShaderNodeMix") weather_over_mix_n.name = weather_over_mix_n.label = Sky.WEATHER_OVER_MIX_NODE - weather_over_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 700) + weather_over_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y - 250) # +300 + weather_over_mix_n.data_type = "RGBA" weather_over_mix_n.blend_type = "MIX" - weather_over_alpha_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + weather_over_alpha_mix_n = node_tree.nodes.new("ShaderNodeMix") weather_over_alpha_mix_n.name = weather_over_alpha_mix_n.label = Sky.WEATHER_OVER_A_MIX_NODE - weather_over_alpha_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 500) + weather_over_alpha_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y - 500) # +50 + weather_over_alpha_mix_n.data_type = "RGBA" weather_over_alpha_mix_n.blend_type = "MIX" - weather_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + weather_mix_n = node_tree.nodes.new("ShaderNodeMix") weather_mix_n.name = weather_mix_n.label = Sky.WEATHER_MIX_NODE - weather_mix_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 1100) + weather_mix_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 500) # +850 + weather_mix_n.data_type = "RGBA" weather_mix_n.blend_type = "MIX" - weather_alpha_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + weather_alpha_mix_n = node_tree.nodes.new("ShaderNodeMix") weather_alpha_mix_n.name = weather_alpha_mix_n.label = Sky.WEATHER_A_MIX_NODE - weather_alpha_mix_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 900) + weather_alpha_mix_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 250) # +600 + weather_alpha_mix_n.data_type = "RGBA" weather_alpha_mix_n.blend_type = "MIX" weather_diff_mult_n = node_tree.nodes.new("ShaderNodeVectorMath") @@ -156,7 +162,7 @@ def init(node_tree): opacity_stars_mix_n = node_tree.nodes.new("ShaderNodeVectorMath") opacity_stars_mix_n.name = opacity_stars_mix_n.label = Sky.OPACITY_STARS_MIX_NODE - opacity_stars_mix_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 900) + opacity_stars_mix_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 700) opacity_stars_mix_n.operation = "MULTIPLY" opacity_stars_mix_n.inputs[1].default_value = (1.0,) * 3 @@ -179,35 +185,36 @@ def init(node_tree): # pass 3 node_tree.links.new(vcol_mult_n.inputs[0], vcol_group_n.outputs['Vertex Color']) + node_tree.links.new(opacity_stars_mix_n.inputs[1], vcol_group_n.outputs['Vertex Color Alpha']) # pass 4 node_tree.links.new(diff_mult_n.inputs[0], diff_col_n.outputs[0]) node_tree.links.new(diff_mult_n.inputs[1], vcol_mult_n.outputs[0]) - node_tree.links.new(weather_base_mix_n.inputs['Color1'], base_a_tex_final_n.outputs[0]) - node_tree.links.new(weather_base_mix_n.inputs['Color2'], base_b_tex_final_n.outputs[0]) + node_tree.links.new(weather_base_mix_n.inputs['A'], base_a_tex_final_n.outputs['Result']) + node_tree.links.new(weather_base_mix_n.inputs['B'], base_b_tex_final_n.outputs['Result']) - node_tree.links.new(weather_base_alpha_mix_n.inputs['Color1'], base_a_tex_final_alpha_n.outputs[0]) - node_tree.links.new(weather_base_alpha_mix_n.inputs['Color2'], base_b_tex_final_alpha_n.outputs[0]) + node_tree.links.new(weather_base_alpha_mix_n.inputs['A'], base_a_tex_final_alpha_n.outputs['Result']) + node_tree.links.new(weather_base_alpha_mix_n.inputs['B'], base_b_tex_final_alpha_n.outputs['Result']) - node_tree.links.new(weather_over_mix_n.inputs['Color1'], over_a_tex_final_n.outputs[0]) - node_tree.links.new(weather_over_mix_n.inputs['Color2'], over_b_tex_final_n.outputs[0]) + node_tree.links.new(weather_over_mix_n.inputs['A'], over_a_tex_final_n.outputs['Result']) + node_tree.links.new(weather_over_mix_n.inputs['B'], over_b_tex_final_n.outputs['Result']) - node_tree.links.new(weather_over_alpha_mix_n.inputs['Color1'], over_a_tex_final_alpha_n.outputs[0]) - node_tree.links.new(weather_over_alpha_mix_n.inputs['Color2'], over_b_tex_final_alpha_n.outputs[0]) + node_tree.links.new(weather_over_alpha_mix_n.inputs['A'], over_a_tex_final_alpha_n.outputs['Result']) + node_tree.links.new(weather_over_alpha_mix_n.inputs['B'], over_b_tex_final_alpha_n.outputs['Result']) # pass 5 - node_tree.links.new(weather_mix_n.inputs['Color1'], weather_base_mix_n.outputs[0]) - node_tree.links.new(weather_mix_n.inputs['Color2'], weather_over_mix_n.outputs[0]) + node_tree.links.new(weather_mix_n.inputs['A'], weather_base_mix_n.outputs['Result']) + node_tree.links.new(weather_mix_n.inputs['B'], weather_over_mix_n.outputs['Result']) - node_tree.links.new(weather_alpha_mix_n.inputs['Color1'], weather_base_alpha_mix_n.outputs[0]) - node_tree.links.new(weather_alpha_mix_n.inputs['Color2'], weather_over_alpha_mix_n.outputs[0]) + node_tree.links.new(weather_alpha_mix_n.inputs['A'], weather_base_alpha_mix_n.outputs['Result']) + node_tree.links.new(weather_alpha_mix_n.inputs['B'], weather_over_alpha_mix_n.outputs['Result']) # pass 6 node_tree.links.new(weather_diff_mult_n.inputs[0], diff_mult_n.outputs[0]) - node_tree.links.new(weather_diff_mult_n.inputs[1], weather_mix_n.outputs[0]) + node_tree.links.new(weather_diff_mult_n.inputs[1], weather_mix_n.outputs['Result']) - node_tree.links.new(opacity_stars_mix_n.inputs[0], weather_alpha_mix_n.outputs[0]) + node_tree.links.new(opacity_stars_mix_n.inputs[0], weather_alpha_mix_n.outputs['Result']) # pass 7 node_tree.links.new(out_shader_node.inputs['Color'], weather_diff_mult_n.outputs[0]) @@ -244,17 +251,24 @@ def __create_texel_component_nodes__(node_tree, tex_type, location): tex_n.location = (location[0], location[1]) tex_n.width = 140 - mix_col_n = node_tree.nodes.new("ShaderNodeMixRGB") + mask_n = node_tree.nodes.new("ShaderNodeTexImage") + mask_n.name = mask_n.label = Sky.MASK_NODE_PREFIX + tex_type + mask_n.location = (location[0], location[1] - 280) + mask_n.width = 140 + + mix_col_n = node_tree.nodes.new("ShaderNodeMix") mix_col_n.name = mix_col_n.label = Sky.TEX_FINAL_MIX_NODE_PREFIX + tex_type - mix_col_n.location = (location[0] + pos_x_shift * 2, location[1] + 100) + mix_col_n.location = (location[0] + pos_x_shift * 2, location[1] + 150) + mix_col_n.data_type = "RGBA" mix_col_n.blend_type = "MIX" - mix_col_n.inputs['Color2'].default_value = (0.0,) * 4 + mix_col_n.inputs['B'].default_value = (0.0,) * 4 - mix_a_n = node_tree.nodes.new("ShaderNodeMixRGB") + mix_a_n = node_tree.nodes.new("ShaderNodeMix") mix_a_n.name = mix_a_n.label = Sky.TEX_FINAL_A_MIX_NODE_PREFIX + tex_type mix_a_n.location = (location[0] + pos_x_shift * 2, location[1] - 100) + mix_a_n.data_type = "RGBA" mix_a_n.blend_type = "MIX" - mix_a_n.inputs['Color2'].default_value = (0.0,) * 4 + mix_a_n.inputs['B'].default_value = (0.0,) * 4 @staticmethod def __create_texel_component_links__(node_tree, tex_type, uv_socket): @@ -271,6 +285,7 @@ def __create_texel_component_links__(node_tree, tex_type, uv_socket): separate_uv_n = node_tree.nodes[Sky.SEPARATE_UV_NODE_PREFIX + tex_type] texel_obb_n = node_tree.nodes[Sky.TEX_OOB_BOOL_NODE_PREFIX + tex_type] tex_n = node_tree.nodes[Sky.TEX_NODE_PREFIX + tex_type] + mask_n = node_tree.nodes[Sky.MASK_NODE_PREFIX + tex_type] mix_col_n = node_tree.nodes[Sky.TEX_FINAL_MIX_NODE_PREFIX + tex_type] mix_a_n = node_tree.nodes[Sky.TEX_FINAL_A_MIX_NODE_PREFIX + tex_type] @@ -278,11 +293,12 @@ def __create_texel_component_links__(node_tree, tex_type, uv_socket): node_tree.links.new(texel_obb_n.inputs[0], separate_uv_n.outputs['Y']) node_tree.links.new(tex_n.inputs[0], uv_socket) + node_tree.links.new(mask_n.inputs[0], uv_socket) - node_tree.links.new(mix_col_n.inputs['Fac'], texel_obb_n.outputs[0]) - node_tree.links.new(mix_col_n.inputs['Color1'], tex_n.outputs['Color']) - node_tree.links.new(mix_a_n.inputs['Fac'], texel_obb_n.outputs[0]) - node_tree.links.new(mix_a_n.inputs['Color1'], tex_n.outputs['Alpha']) + node_tree.links.new(mix_col_n.inputs['Factor'], texel_obb_n.outputs[0]) + node_tree.links.new(mix_col_n.inputs['A'], tex_n.outputs['Color']) + node_tree.links.new(mix_a_n.inputs['Factor'], texel_obb_n.outputs[0]) + node_tree.links.new(mix_a_n.inputs['A'], mask_n.outputs['Color']) @staticmethod def finalize(node_tree, material): @@ -294,15 +310,22 @@ def finalize(node_tree, material): :type material: bpy.types.Material """ + out_shader_node = node_tree.nodes[Sky.OUT_SHADER_NODE] + material.use_backface_culling = True - material.blend_method = "BLEND" + out_shader_node.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" + + + if sky_bottom.is_set(node_tree): + out_shader_node.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if sky_stars.is_set(node_tree): - material.blend_method = "BLEND" - if sky_back.is_set(node_tree): - material.blend_method = "OPAQUE" + out_shader_node.inputs["Alpha Type"].default_value = -1.0 + material.surface_render_method = "DITHERED" - if material.blend_method == "OPAQUE" and node_tree.nodes[Sky.OUT_SHADER_NODE].inputs['Alpha'].links: + if out_shader_node.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[Sky.OUT_SHADER_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[Sky.OUT_SHADER_NODE].inputs['Alpha'].links[0]) @staticmethod @@ -319,6 +342,10 @@ def set_diffuse(node_tree, color): node_tree.nodes[Sky.DIFF_COL_NODE].outputs['Color'].default_value = color + ########################## + #### BASE A/B #### + ########################## + @staticmethod def set_sky_weather_base_a_texture(node_tree, image): """Set base_a texture to shader. @@ -392,6 +419,10 @@ def set_sky_weather_base_b_uv(node_tree, uv_layer): Sky.set_sky_weather_base_a_uv(node_tree, uv_layer) + ########################## + #### OVER A/B #### + ########################## + @staticmethod def set_sky_weather_over_a_texture(node_tree, image): """Set over_a texture to shader. @@ -462,6 +493,158 @@ def set_sky_weather_over_b_uv(node_tree, uv_layer): Sky.set_sky_weather_base_a_uv(node_tree, uv_layer) + ########################## + #### BASE MASK A/B #### + ########################## + + @staticmethod + def set_sky_weather_base_mask_a_texture(node_tree, image): + """Set base_mask_a texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_base_a()].image = image + + @staticmethod + def set_sky_weather_base_mask_a_texture_settings(node_tree, settings): + """Set base_mask_a texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_base_a()], settings) + + @staticmethod + def set_sky_weather_base_mask_a_uv(node_tree, uv_layer): + """Set UV layer to base_mask_a texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base_mask_a texture + :type uv_layer: str + """ + + Sky.set_sky_weather_base_a_uv(node_tree, uv_layer) + + @staticmethod + def set_sky_weather_base_mask_b_texture(node_tree, image): + """Set base_mask_b texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_base_b()].image = image + + @staticmethod + def set_sky_weather_base_mask_b_texture_settings(node_tree, settings): + """Set base_mask_b texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_base_b()], settings) + + @staticmethod + def set_sky_weather_base_mask_b_uv(node_tree, uv_layer): + """Set UV layer to base_mask_b texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for base_mask_b texture + :type uv_layer: str + """ + + Sky.set_sky_weather_base_a_uv(node_tree, uv_layer) + + ########################## + #### OVER MASK A/B #### + ########################## + + @staticmethod + def set_sky_weather_over_mask_a_texture(node_tree, image): + """Set over_mask_a texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_over_a()].image = image + + @staticmethod + def set_sky_weather_over_mask_a_texture_settings(node_tree, settings): + """Set over_mask_a texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_over_a()], settings) + + @staticmethod + def set_sky_weather_over_mask_a_uv(node_tree, uv_layer): + """Set UV layer to over_mask_a texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over_mask_a texture + :type uv_layer: str + """ + + Sky.set_sky_weather_base_a_uv(node_tree, uv_layer) + + @staticmethod + def set_sky_weather_over_mask_b_texture(node_tree, image): + """Set over_mask_b texture to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param image: texture image which should be assigned to texture node + :type image: bpy.types.Image + """ + + node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_over_b()].image = image + + @staticmethod + def set_sky_weather_over_mask_b_texture_settings(node_tree, settings): + """Set over_mask_b texture settings to shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param settings: binary string of TOBJ settings gotten from tobj import + :type settings: str + """ + _material_utils.set_texture_settings_to_node(node_tree.nodes[Sky.MASK_NODE_PREFIX + texture_types.get_over_b()], settings) + + @staticmethod + def set_sky_weather_over_mask_b_uv(node_tree, uv_layer): + """Set UV layer to over_mask_b texture in shader. + + :param node_tree: node tree of current shader + :type node_tree: bpy.types.NodeTree + :param uv_layer: uv layer string used for over_mask_b texture + :type uv_layer: str + """ + + Sky.set_sky_weather_base_a_uv(node_tree, uv_layer) + + ########################## + #### AUX/FLAVORS #### + ########################## + @staticmethod def set_aux0(node_tree, aux_property): """Set blend factors. @@ -474,15 +657,15 @@ def set_aux0(node_tree, aux_property): profile_blend = _math_utils.clamp(aux_property[0]['value']) - node_tree.nodes[Sky.WEATHER_BASE_MIX_NODE].inputs['Fac'].default_value = profile_blend - node_tree.nodes[Sky.WEATHER_BASE_A_MIX_NODE].inputs['Fac'].default_value = profile_blend - node_tree.nodes[Sky.WEATHER_OVER_MIX_NODE].inputs['Fac'].default_value = profile_blend - node_tree.nodes[Sky.WEATHER_OVER_A_MIX_NODE].inputs['Fac'].default_value = profile_blend + node_tree.nodes[Sky.WEATHER_BASE_MIX_NODE].inputs['Factor'].default_value = profile_blend + node_tree.nodes[Sky.WEATHER_BASE_A_MIX_NODE].inputs['Factor'].default_value = profile_blend + node_tree.nodes[Sky.WEATHER_OVER_MIX_NODE].inputs['Factor'].default_value = profile_blend + node_tree.nodes[Sky.WEATHER_OVER_A_MIX_NODE].inputs['Factor'].default_value = profile_blend weather_blend = _math_utils.clamp(aux_property[1]['value']) - node_tree.nodes[Sky.WEATHER_MIX_NODE].inputs['Fac'].default_value = weather_blend - node_tree.nodes[Sky.WEATHER_A_MIX_NODE].inputs['Fac'].default_value = weather_blend + node_tree.nodes[Sky.WEATHER_MIX_NODE].inputs['Factor'].default_value = weather_blend + node_tree.nodes[Sky.WEATHER_A_MIX_NODE].inputs['Factor'].default_value = weather_blend if sky_stars.is_set(node_tree): stars_opacity = (_math_utils.clamp(aux_property[2]['value']),) * 3 @@ -503,7 +686,7 @@ def set_aux1(node_tree, aux_property): for tex_type_i, tex_type in enumerate(texture_types.get()): - if (not sky_stars.is_set(node_tree)) and (not sky_back.is_set(node_tree)): # enabled + if (not sky_stars.is_set(node_tree)) and (not sky_bottom.is_set(node_tree)): # enabled v_cutoff = aux_property[tex_type_i]['value'] else: # disabled v_cutoff = float("-inf") @@ -530,8 +713,8 @@ def set_aux2(node_tree, aux_property): node_tree.nodes[Sky.RESCALE_UV_GROUP_NODE].inputs['Rescale Enabled'].default_value = rescale_enabled @staticmethod - def set_sky_stars_flavor(node_tree, switch_on): - """Set sky stars flavor to this shader. + def set_sky_bottom_flavor(node_tree, switch_on): + """Set sky bottom flavor to this shader. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -540,13 +723,13 @@ def set_sky_stars_flavor(node_tree, switch_on): """ if switch_on: - sky_stars.init(node_tree) + sky_bottom.init(node_tree) else: - sky_stars.delete(node_tree) - + sky_bottom.delete(node_tree) + @staticmethod - def set_sky_back_flavor(node_tree, switch_on): - """Set sky back flavor to this shader. + def set_sky_stars_flavor(node_tree, switch_on): + """Set sky stars flavor to this shader. :param node_tree: node tree of current shader :type node_tree: bpy.types.NodeTree @@ -555,6 +738,7 @@ def set_sky_back_flavor(node_tree, switch_on): """ if switch_on: - sky_back.init(node_tree) + sky_stars.init(node_tree) else: - sky_back.delete(node_tree) + sky_stars.delete(node_tree) + diff --git a/addon/io_scs_tools/internals/shaders/eut2/sky/uv_rescale_ng.py b/addon/io_scs_tools/internals/shaders/eut2/sky/uv_rescale_ng.py index 34b1eedf..6472ab2f 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/sky/uv_rescale_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/sky/uv_rescale_ng.py @@ -58,24 +58,27 @@ def __create_node_group__(): rescale_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=SKY_UV_RESCALE_G) # inputs defining - rescale_g.inputs.new("NodeSocketFloat", "Rescale Enabled") - rescale_g.inputs.new("NodeSocketFloat", "V Scale Base A") - rescale_g.inputs.new("NodeSocketFloat", "V Scale Base B") - rescale_g.inputs.new("NodeSocketFloat", "V Scale Over A") - rescale_g.inputs.new("NodeSocketFloat", "V Scale Over B") - rescale_g.inputs.new("NodeSocketVector", "UV") + rescale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Rescale Enabled") + rescale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "V Scale Base A") + rescale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "V Scale Base B") + rescale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "V Scale Over A") + rescale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "V Scale Over B") + rescale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "UV") + + # outputs defining + rescale_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV Base A") + rescale_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV Base B") + rescale_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV Over A") + rescale_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV Over B") + + + # create nodes input_n = rescale_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - rescale_g.outputs.new("NodeSocketVector", "UV Base A") - rescale_g.outputs.new("NodeSocketVector", "UV Base B") - rescale_g.outputs.new("NodeSocketVector", "UV Over A") - rescale_g.outputs.new("NodeSocketVector", "UV Over B") output_n = rescale_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 9, 0) - # create nodes rescale_inv_n = rescale_g.nodes.new("ShaderNodeMath") rescale_inv_n.name = rescale_inv_n.label = _RESCALE_INV_NODE rescale_inv_n.location = (pos_x_shift * 2, 100) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env_ng.py index d46d2026..742b6086 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/add_env_ng.py @@ -124,21 +124,22 @@ def __create_node_group__(): add_env_g.nodes.clear() # inputs defining - add_env_g.inputs.new("NodeSocketFloat", "Fresnel Type") - add_env_g.inputs.new("NodeSocketFloat", "Fresnel Scale") - add_env_g.inputs.new("NodeSocketFloat", "Fresnel Bias") - add_env_g.inputs.new("NodeSocketVector", "Normal Vector") - add_env_g.inputs.new("NodeSocketVector", "Reflection Normal Vector") - add_env_g.inputs.new("NodeSocketFloat", "Apply Fresnel") - add_env_g.inputs.new("NodeSocketColor", "Reflection Texture Color") - add_env_g.inputs.new("NodeSocketFloat", "Base Texture Alpha") - add_env_g.inputs.new("NodeSocketColor", "Env Factor Color") - add_env_g.inputs.new("NodeSocketColor", "Specular Color") - add_env_g.inputs.new("NodeSocketColor", "Weighted Color") - add_env_g.inputs.new("NodeSocketFloat", "Strength Multiplier") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Fresnel Type") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Fresnel Scale") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Fresnel Bias") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Normal Vector") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Reflection Normal Vector") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Apply Fresnel") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Reflection Texture Color") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Base Texture Alpha") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Env Factor Color") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Specular Color") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Weighted Color") + add_env_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Strength Multiplier") # outputs defining - add_env_g.outputs.new("NodeSocketColor", "Environment Addition Color") + add_env_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Environment Addition Color") + # node creation input_n = add_env_g.nodes.new("NodeGroupInput") @@ -175,9 +176,10 @@ def __create_node_group__(): refl_tex_mult_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y - 150) refl_tex_mult_n.operation = "MULTIPLY" - fresnel_type_mix_n = add_env_g.nodes.new("ShaderNodeMixRGB") + fresnel_type_mix_n = add_env_g.nodes.new("ShaderNodeMix") fresnel_type_mix_n.name = fresnel_type_mix_n.label = _FRESNEL_TYPE_MIX_NODE fresnel_type_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 200) + fresnel_type_mix_n.data_type = "RGBA" fresnel_type_mix_n.blend_type = "MIX" refl_tex_col_mult_n = add_env_g.nodes.new("ShaderNodeVectorMath") @@ -185,14 +187,15 @@ def __create_node_group__(): refl_tex_col_mult_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y - 200) refl_tex_col_mult_n.operation = "MULTIPLY" - tex_fresnel_mult_n = add_env_g.nodes.new("ShaderNodeMixRGB") + tex_fresnel_mult_n = add_env_g.nodes.new("ShaderNodeMix") tex_fresnel_mult_n.name = tex_fresnel_mult_n.label = _TEX_FRESNEL_MULT_NODE tex_fresnel_mult_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y) + tex_fresnel_mult_n.data_type = "RGBA" tex_fresnel_mult_n.blend_type = "MULTIPLY" global_env_factor_n = add_env_g.nodes.new("ShaderNodeValue") global_env_factor_n.name = global_env_factor_n.label = _GLOBAL_ENV_FACTOR_NODE - global_env_factor_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y - 200) + global_env_factor_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y - 250) global_env_mult_n = add_env_g.nodes.new("ShaderNodeVectorMath") global_env_mult_n.name = global_env_mult_n.label = _GLOBAL_ENV_MULT_NODE @@ -226,20 +229,20 @@ def __create_node_group__(): add_env_g.links.new(fresnel_schlick_n.inputs['Bias'], input_n.outputs['Fresnel Bias']) # pass 3 - add_env_g.links.new(fresnel_type_mix_n.inputs['Fac'], input_n.outputs['Fresnel Type']) - add_env_g.links.new(fresnel_type_mix_n.inputs['Color1'], fresnel_legacy_n.outputs['Fresnel Factor']) - add_env_g.links.new(fresnel_type_mix_n.inputs['Color2'], fresnel_schlick_n.outputs['Fresnel Factor']) + add_env_g.links.new(fresnel_type_mix_n.inputs['Factor'], input_n.outputs['Fresnel Type']) + add_env_g.links.new(fresnel_type_mix_n.inputs['A'], fresnel_legacy_n.outputs['Fresnel Factor']) + add_env_g.links.new(fresnel_type_mix_n.inputs['B'], fresnel_schlick_n.outputs['Fresnel Factor']) add_env_g.links.new(refl_tex_col_mult_n.inputs[0], refl_tex_mult_n.outputs[0]) add_env_g.links.new(refl_tex_col_mult_n.inputs[1], env_spec_mult_n.outputs[0]) # pass 4 - add_env_g.links.new(tex_fresnel_mult_n.inputs['Fac'], input_n.outputs['Apply Fresnel']) - add_env_g.links.new(tex_fresnel_mult_n.inputs['Color1'], refl_tex_col_mult_n.outputs[0]) - add_env_g.links.new(tex_fresnel_mult_n.inputs['Color2'], fresnel_type_mix_n.outputs['Color']) + add_env_g.links.new(tex_fresnel_mult_n.inputs['Factor'], input_n.outputs['Apply Fresnel']) + add_env_g.links.new(tex_fresnel_mult_n.inputs['A'], refl_tex_col_mult_n.outputs[0]) + add_env_g.links.new(tex_fresnel_mult_n.inputs['B'], fresnel_type_mix_n.outputs['Result']) # pass 5 - add_env_g.links.new(global_env_mult_n.inputs[0], tex_fresnel_mult_n.outputs['Color']) + add_env_g.links.new(global_env_mult_n.inputs[0], tex_fresnel_mult_n.outputs['Result']) add_env_g.links.new(global_env_mult_n.inputs[1], global_env_factor_n.outputs['Value']) # pass 6 diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/alpha_remap_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/alpha_remap_ng.py index 6c0b41e0..3db19762 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/alpha_remap_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/alpha_remap_ng.py @@ -50,14 +50,18 @@ def __create_node_group__(): asafew_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=ASAFEW_G) # inputs defining - asafew_g.inputs.new("NodeSocketFloat", "Alpha") - asafew_g.inputs.new("NodeSocketFloat", "Factor1") - asafew_g.inputs.new("NodeSocketFloat", "Factor2") + asafew_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Alpha") + asafew_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Factor1") + asafew_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Factor2") + + # outputs defining + asafew_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Weighted Alpha") + + + # node creation input_n = asafew_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - asafew_g.outputs.new("NodeSocketFloat", "Weighted Alpha") output_n = asafew_g.nodes.new("NodeGroupOutput") output_n.location = (185 * 3, 0) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting_ng.py index 07966c7a..d6904482 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/compose_lighting_ng.py @@ -88,24 +88,28 @@ def __create_node_group__(): compose_light_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=COMPOSE_LIGHTING_G) # inputs defining - compose_light_g.inputs.new("NodeSocketFloat", "AddAmbient") - compose_light_g.inputs.new("NodeSocketColor", "Diffuse Color") - compose_light_g.inputs.new("NodeSocketColor", "Specular Color") - compose_light_g.inputs.new("NodeSocketColor", "Env Color") - compose_light_g.inputs.new("NodeSocketColor", "Diffuse Lighting") - compose_light_g.inputs.new("NodeSocketColor", "Specular Lighting") - compose_light_g.inputs.new("NodeSocketFloat", "Alpha") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "AddAmbient") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Diffuse Color") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Specular Color") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Env Color") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Diffuse Lighting") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Specular Lighting") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Alpha") + compose_light_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Alpha Type", description = "-1 = OPAQUE\n0 = CLIP\n1 = BLEND") + + # outputs defining + compose_light_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketShader", name = "Shader") + compose_light_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Color") + compose_light_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Alpha") + + + # nodes creation input_n = compose_light_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, 0) - # outputs defining - compose_light_g.outputs.new("NodeSocketShader", "Shader") - compose_light_g.outputs.new("NodeSocketColor", "Color") - compose_light_g.outputs.new("NodeSocketFloat", "Alpha") output_n = compose_light_g.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 8, 0) - # nodes creation add_ambient_col_n = compose_light_g.nodes.new("ShaderNodeRGB") add_ambient_col_n.name = add_ambient_col_n.label = _ADD_AMBIENT_COL_NODE add_ambient_col_n.location = (start_pos_x + pos_x_shift * 1, 400) @@ -166,6 +170,7 @@ def __create_node_group__(): compose_light_g.links.new(out_mat_node.inputs["Color"], sum_final_n.outputs["Vector"]) compose_light_g.links.new(out_mat_node.inputs["Alpha"], input_n.outputs["Alpha"]) + compose_light_g.links.new(out_mat_node.inputs["Alpha Type"], input_n.outputs["Alpha Type"]) compose_light_g.links.new(output_n.inputs["Shader"], out_mat_node.outputs["Shader"]) compose_light_g.links.new(output_n.inputs["Color"], sum_final_n.outputs["Vector"]) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_legacy_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_legacy_ng.py index 7abd9eab..7a3db7e9 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_legacy_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_legacy_ng.py @@ -47,19 +47,22 @@ def __create_fresnel_group__(): fresnel_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=FRESNEL_LEGACY_G) # inputs defining - fresnel_g.inputs.new("NodeSocketFloat", "Scale") - fresnel_g.inputs.new("NodeSocketFloat", "Bias") - fresnel_g.inputs.new("NodeSocketVector", "Normal Vector") - fresnel_g.inputs.new("NodeSocketVector", "Reflection Normal Vector") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Scale") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Bias") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Normal Vector") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Reflection Normal Vector") + + # outputs defining + fresnel_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Fresnel Factor") + + + # group nodes input_n = fresnel_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - fresnel_g.outputs.new("NodeSocketFloat", "Fresnel Factor") output_n = fresnel_g.nodes.new("NodeGroupOutput") output_n.location = (185 * 5, 0) - # group nodes dot_n = fresnel_g.nodes.new("ShaderNodeVectorMath") dot_n.location = (185, 100) dot_n.operation = "DOT_PRODUCT" diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_schlick_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_schlick_ng.py index fa5233cb..88e71a0f 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_schlick_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/fresnel_schlick_ng.py @@ -49,18 +49,21 @@ def __create_fresnel_group__(): pos_x_shift = 185 # inputs defining - fresnel_g.inputs.new("NodeSocketFloat", "Bias") - fresnel_g.inputs.new("NodeSocketVector", "Normal Vector") - fresnel_g.inputs.new("NodeSocketVector", "Reflection Normal Vector") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Bias") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Normal Vector") + fresnel_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Reflection Normal Vector") + + # outputs defining + fresnel_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Fresnel Factor") + + + # group nodes input_n = fresnel_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - fresnel_g.outputs.new("NodeSocketFloat", "Fresnel Factor") output_n = fresnel_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 6, 0) - # group nodes dot_n = fresnel_g.nodes.new("ShaderNodeVectorMath") dot_n.location = (pos_x_shift, 100) dot_n.operation = "DOT_PRODUCT" diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lampmask_mixer_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lampmask_mixer_ng.py index 15da572b..5542ca7b 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lampmask_mixer_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lampmask_mixer_ng.py @@ -27,7 +27,7 @@ AUX_LAMP_TYPES = _LT_consts.AuxiliaryLampTypes TRAFFIC_LIGHT_TYPES = _LT_consts.TrafficLightTypes -UV_X_TILES = ["UV_X_0_", "UV_X_1_", "UV_X_2_", "UV_X_3_", "UV_X_4_"] +UV_X_TILES = ["UV_X_0_", "UV_X_1_", "UV_X_2_", "UV_X_3_", "UV_X_4_", "UV_X_5_"] UV_Y_TILES = ["UV_Y_0_", "UV_Y_1_", "UV_Y_2_", "UV_Y_3_"] LAMPMASK_MIX_G = _MAT_consts.node_group_prefix + "LampmaskMixerGroup" @@ -66,50 +66,53 @@ def __create_node_group__(): lampmask_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=LAMPMASK_MIX_G) # inputs defining - lampmask_g.inputs.new("NodeSocketFloat", "Lampmask Tex Alpha") - lampmask_g.inputs.new("NodeSocketColor", "Lampmask Tex Color") - lampmask_g.inputs.new("NodeSocketVector", "UV Vector") + lampmask_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Lampmask Tex Alpha") + lampmask_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Lampmask Tex Color") + lampmask_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "UV Vector") + + # outputs defining + lampmask_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Lampmask Addition Color") + + + # nodes creation input_n = lampmask_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - lampmask_g.outputs.new("NodeSocketColor", "Lampmask Addition Color") output_n = lampmask_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 9, 0) - # nodes creation alpha_decode_n = lampmask_g.nodes.new("ShaderNodeMath") alpha_decode_n.name = alpha_decode_n.label = _TEX_COL_SEP_NODE alpha_decode_n.location = (pos_x_shift, 50) alpha_decode_n.operation = "POWER" alpha_decode_n.inputs[1].default_value = 2.2 - tex_col_sep_n = lampmask_g.nodes.new("ShaderNodeSeparateRGB") + tex_col_sep_n = lampmask_g.nodes.new("ShaderNodeSeparateColor") tex_col_sep_n.name = tex_col_sep_n.label = _TEX_COL_SEP_NODE tex_col_sep_n.location = (pos_x_shift, 400) uv_x_dot_n = lampmask_g.nodes.new("ShaderNodeVectorMath") uv_x_dot_n.name = uv_x_dot_n.label = _UV_DOT_X_NODE - uv_x_dot_n.location = (pos_x_shift, -200) + uv_x_dot_n.location = (pos_x_shift, -400) uv_x_dot_n.operation = "DOT_PRODUCT" uv_x_dot_n.inputs[1].default_value = (1.0, 0, 0) uv_y_dot_n = lampmask_g.nodes.new("ShaderNodeVectorMath") uv_y_dot_n.name = uv_y_dot_n.label = _UV_DOT_Y_NODE - uv_y_dot_n.location = (pos_x_shift, -450) + uv_y_dot_n.location = (pos_x_shift, -750) uv_y_dot_n.operation = "DOT_PRODUCT" uv_y_dot_n.inputs[1].default_value = (0, 1.0, 0) # links creation lampmask_g.links.new(alpha_decode_n.inputs[0], input_n.outputs['Lampmask Tex Alpha']) - lampmask_g.links.new(tex_col_sep_n.inputs['Image'], input_n.outputs['Lampmask Tex Color']) + lampmask_g.links.new(tex_col_sep_n.inputs['Color'], input_n.outputs['Lampmask Tex Color']) lampmask_g.links.new(uv_x_dot_n.inputs[0], input_n.outputs['UV Vector']) lampmask_g.links.new(uv_y_dot_n.inputs[0], input_n.outputs['UV Vector']) nodes_for_addition = [] # init uv tilling mechanism - pos_y = -100 + pos_y = -300 max_x_uv = 1 for uv_x_tile in UV_X_TILES: @@ -119,7 +122,7 @@ def __create_node_group__(): max_x_uv += 1 max_y_uv = 1 - pos_y = -400 + pos_y = -700 for uv_y_tile in UV_Y_TILES: __init_uv_tile_bounding_nodes__(lampmask_g, uv_y_dot_n, uv_y_tile, pos_x_shift * 2, pos_y, max_y_uv) @@ -128,21 +131,21 @@ def __create_node_group__(): max_y_uv += 1 # init vehicle sides uv bounding mechanism - pos_y = -50 + pos_y = -250 for vehicle_side in VEHICLE_SIDES: __init_vehicle_uv_bounding_nodes__(lampmask_g, vehicle_side, pos_x_shift * 2, pos_y) pos_y -= 50 # init traffic light uv bounding mechanism - pos_y = -350 + pos_y = -600 for traffic_light_type in TRAFFIC_LIGHT_TYPES: __init_traffic_light_uv_bounding_nodes__(lampmask_g, traffic_light_type, pos_x_shift * 2, pos_y) pos_y -= 100 # init vehicle sides switches mechanism - pos_y = 1000 + pos_y = 1200 for vehicle_lamp_type in VEHICLE_LAMP_TYPES: if vehicle_lamp_type == VEHICLE_LAMP_TYPES.Positional: # make extra space for positional @@ -150,13 +153,13 @@ def __create_node_group__(): __init_vehicle_switch_nodes__(lampmask_g, alpha_decode_n.outputs[0], - tex_col_sep_n.outputs["R"], - tex_col_sep_n.outputs["G"], - tex_col_sep_n.outputs["B"], + tex_col_sep_n.outputs["Red"], + tex_col_sep_n.outputs["Green"], + tex_col_sep_n.outputs["Blue"], vehicle_lamp_type, pos_x_shift * 5, pos_y, nodes_for_addition) - pos_y -= 75 + pos_y -= 125 # init auxiliary lamp switches mechanism pos_y -= 60 @@ -164,8 +167,8 @@ def __create_node_group__(): __init_aux_switch_nodes__(lampmask_g, alpha_decode_n.outputs[0], - tex_col_sep_n.outputs["R"], - tex_col_sep_n.outputs["G"], + tex_col_sep_n.outputs["Red"], + tex_col_sep_n.outputs["Green"], aux_lamp_type, pos_x_shift * 5, pos_y, nodes_for_addition) @@ -194,7 +197,7 @@ def __create_node_group__(): add_n = lampmask_g.nodes.new("ShaderNodeMath") add_n.name = _ADD_NODE_PREFIX + str(i) add_n.label = _ADD_NODE_PREFIX + str(i) - add_n.location = (curr_node.location.x + 120, curr_node.location.y) + add_n.location = (curr_node.location.x + 180, curr_node.location.y) add_n.hide = True add_n.operation = "ADD" @@ -275,15 +278,17 @@ def __init_vehicle_uv_bounding_nodes__(node_tree, vehicle_side, pos_x, pos_y): elif vehicle_side == VEHICLE_SIDES.RearRight: min_uv_n = node_tree.nodes[UV_X_TILES[2] + _MAX_UV_SUFFIX] max_uv_n = node_tree.nodes[UV_X_TILES[3] + _MAX_UV_SUFFIX] - else: # fallback to middle + elif vehicle_side == VEHICLE_SIDES.MiddleLeft: min_uv_n = node_tree.nodes[UV_X_TILES[3] + _MAX_UV_SUFFIX] max_uv_n = node_tree.nodes[UV_X_TILES[4] + _MAX_UV_SUFFIX] + else: # fallback to MiddleRight + min_uv_n = node_tree.nodes[UV_X_TILES[4] + _MAX_UV_SUFFIX] + max_uv_n = node_tree.nodes[UV_X_TILES[5] + _MAX_UV_SUFFIX] uv_in_bounds_n = node_tree.nodes.new("ShaderNodeMath") uv_in_bounds_n.name = vehicle_side.name + _IN_BOUNDS_SUFFIX uv_in_bounds_n.label = vehicle_side.name + _IN_BOUNDS_SUFFIX uv_in_bounds_n.location = (pos_x + 185, pos_y - 50) - uv_in_bounds_n.width_hidden = 100 uv_in_bounds_n.hide = True uv_in_bounds_n.operation = "LESS_THAN" @@ -321,7 +326,6 @@ def __init_traffic_light_uv_bounding_nodes__(node_tree, traffic_light_type, pos_ uv_x_in_bounds_n.name = traffic_light_type.name + "X" + _IN_BOUNDS_SUFFIX uv_x_in_bounds_n.label = traffic_light_type.name + "X" + _IN_BOUNDS_SUFFIX uv_x_in_bounds_n.location = (pos_x + 185, pos_y) - uv_x_in_bounds_n.width_hidden = 100 uv_x_in_bounds_n.hide = True uv_x_in_bounds_n.operation = "LESS_THAN" @@ -329,7 +333,6 @@ def __init_traffic_light_uv_bounding_nodes__(node_tree, traffic_light_type, pos_ uv_y_in_bounds_n.name = traffic_light_type.name + "Y" + _IN_BOUNDS_SUFFIX uv_y_in_bounds_n.label = traffic_light_type.name + "Y" + _IN_BOUNDS_SUFFIX uv_y_in_bounds_n.location = (pos_x + 185, pos_y - 50) - uv_y_in_bounds_n.width_hidden = 100 uv_y_in_bounds_n.hide = True uv_y_in_bounds_n.operation = "LESS_THAN" @@ -337,7 +340,6 @@ def __init_traffic_light_uv_bounding_nodes__(node_tree, traffic_light_type, pos_ uv_in_bounds_n.name = traffic_light_type.name + _IN_BOUNDS_SUFFIX uv_in_bounds_n.label = traffic_light_type.name + _IN_BOUNDS_SUFFIX uv_in_bounds_n.location = (pos_x + 185 * 2, pos_y) - uv_in_bounds_n.width_hidden = 100 uv_in_bounds_n.hide = True uv_in_bounds_n.operation = "MULTIPLY" @@ -386,7 +388,6 @@ def __init_vehicle_switch_nodes__(node_tree, a_output, r_output, g_output, b_out switch_n.name = lamp_type.name switch_n.label = lamp_type.name switch_n.location = (pos_x, pos_y) - switch_n.width_hidden = 100 switch_n.hide = True switch_n.operation = "MULTIPLY" switch_n.inputs[0].default_value = 0.0 @@ -410,25 +411,33 @@ def __init_vehicle_switch_nodes__(node_tree, a_output, r_output, g_output, b_out elif lamp_type == VEHICLE_LAMP_TYPES.DRL: node_tree.links.new(switch_n.inputs[1], b_output) - node_name = lamp_type.name + VEHICLE_SIDES.Middle.name - position = (pos_x + 185 * 2, pos_y) - in_bounds_n = node_tree.nodes[VEHICLE_SIDES.Middle.name + _IN_BOUNDS_SUFFIX] + in_bounds_n = node_tree.nodes[VEHICLE_SIDES.MiddleLeft.name + _IN_BOUNDS_SUFFIX] + node_name = lamp_type.name + VEHICLE_SIDES.MiddleLeft.name + position = (pos_x + 185 * 2, pos_y + 18) + mult_n = __create_merging_node__(node_tree, node_name, position, in_bounds_n.outputs[0], switch_n.outputs[0]) + nodes_for_addition.append(mult_n) + + in_bounds_n = node_tree.nodes[VEHICLE_SIDES.MiddleRight.name + _IN_BOUNDS_SUFFIX] + node_name = lamp_type.name + VEHICLE_SIDES.MiddleRight.name + position = (pos_x + 185 * 2, pos_y - 18) mult_n = __create_merging_node__(node_tree, node_name, position, in_bounds_n.outputs[0], switch_n.outputs[0]) nodes_for_addition.append(mult_n) else: - color_output = veh_side_name1 = veh_side_name2 = None + color_output = veh_side_name1 = veh_side_name2 = veh_side_name3 = None if lamp_type == VEHICLE_LAMP_TYPES.LeftTurn: color_output = r_output veh_side_name1 = VEHICLE_SIDES.FrontLeft.name veh_side_name2 = VEHICLE_SIDES.RearLeft.name + veh_side_name3 = VEHICLE_SIDES.MiddleLeft.name elif lamp_type == VEHICLE_LAMP_TYPES.RightTurn: color_output = r_output veh_side_name1 = VEHICLE_SIDES.FrontRight.name veh_side_name2 = VEHICLE_SIDES.RearRight.name + veh_side_name3 = VEHICLE_SIDES.MiddleRight.name elif lamp_type == VEHICLE_LAMP_TYPES.Brake: @@ -469,6 +478,13 @@ def __init_vehicle_switch_nodes__(node_tree, a_output, r_output, g_output, b_out mult_n = __create_merging_node__(node_tree, node_name, node_pos, switch_n.outputs[0], in_bounds_n.outputs[0]) nodes_for_addition.append(mult_n) + if veh_side_name3: + in_bounds_n = node_tree.nodes[veh_side_name3 + _IN_BOUNDS_SUFFIX] + node_name = lamp_type.name + veh_side_name3 + node_pos = (pos_x + 185 * 2, pos_y - 54) + mult_n = __create_merging_node__(node_tree, node_name, node_pos, switch_n.outputs[0], in_bounds_n.outputs[0]) + nodes_for_addition.append(mult_n) + def __init_aux_switch_nodes__(node_tree, a_output, r_output, g_output, lamp_type, pos_x, pos_y, nodes_for_addition): """Creation and linking of switching nodes, covering all combinations for vehicle auxiliary lamp mask. @@ -498,7 +514,6 @@ def __init_aux_switch_nodes__(node_tree, a_output, r_output, g_output, lamp_type switch_n.name = lamp_type.name switch_n.label = lamp_type.name switch_n.location = (pos_x, pos_y) - switch_n.width_hidden = 100 switch_n.hide = True switch_n.operation = "MULTIPLY" switch_n.inputs[0].default_value = 0.0 @@ -553,7 +568,6 @@ def __init_traffic_light_switch_nodes__(node_tree, a_output, lamp_type, pos_x, p switch_n.name = lamp_type.name switch_n.label = lamp_type.name switch_n.location = (pos_x, pos_y) - switch_n.width_hidden = 100 switch_n.hide = True switch_n.operation = "MULTIPLY" switch_n.inputs[0].default_value = 0.0 diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lighting_evaluator_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lighting_evaluator_ng.py index 2008440f..793500bd 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lighting_evaluator_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/lighting_evaluator_ng.py @@ -41,7 +41,7 @@ _DIFF_FLAT_MULT_NODE = "DFlat=DiffuseLightColor*0.4" -_DIFF_FLAT_NODE = "FlatFilter" +_DIFF_FLAT_NODE = "DiffFlatFilter" _N_SUM_NODE = "NSum=IncomingVector+NLight" _N_HALF_NODE = "NHalf=normalize(NSum)" @@ -52,7 +52,7 @@ _SPEC_FACTOR_SMOOTH_NODE = "SpecFacSmooth=SpecFac*NDotLSpecCut" # clamp it! _SPEC_MULT_NODE = "S=SpecularLightColor*SpecFacSmooth" # goes to Specular Lighting output -_SPEC_FLAT_NODE = "FlatFilter" +_SPEC_FLAT_NODE = "SpecFlatFilter" def get_node_group(): @@ -155,15 +155,15 @@ def __create_group__(): lighting_eval_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=LIGHTING_EVALUATOR_G) # inputs defining - lighting_eval_g.inputs.new("NodeSocketVector", "Normal Vector") # n_normal - lighting_eval_g.inputs.new("NodeSocketVector", "Incoming Vector") # n_eye - lighting_eval_g.inputs.new("NodeSocketFloat", "Shininess") # specular_exponent - lighting_eval_g.inputs.new("NodeSocketFloat", "Flat Lighting") # flat lighting switch, should be 0 or 1 + lighting_eval_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Normal Vector", description = "n_normal") + lighting_eval_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Incoming Vector", description = "n_eye") + lighting_eval_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Shininess", description = "specular_exponent") + lighting_eval_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Flat Lighting", description = "Flat lighting switch, should be 0 or 1") # outputs defining - lighting_eval_g.outputs.new("NodeSocketColor", "Diffuse Lighting") # final diffuse lighting - lighting_eval_g.outputs.new("NodeSocketColor", "Specular Lighting") # final specular lighting - lighting_eval_g.outputs.new("NodeSocketVector", "Normal") # bypassed normal, to have one access point to final normal + lighting_eval_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Diffuse Lighting", description = "Final Diffuse Lighting") + lighting_eval_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Specular Lighting", description = "Final Specular Lighting") + lighting_eval_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Normal", description = "Bypassed normal, to have one access point to final normal") else: # recreation @@ -231,9 +231,10 @@ def __create_group__(): diff_flat_mult_n.operation = "MULTIPLY" diff_flat_mult_n.inputs[1].default_value = (0.4,) * 3 - diff_flat_n = lighting_eval_g.nodes.new("ShaderNodeMixRGB") + diff_flat_n = lighting_eval_g.nodes.new("ShaderNodeMix") diff_flat_n.name = diff_flat_n.label = _DIFF_FLAT_NODE diff_flat_n.location = (start_pos_x + pos_x_shift * 5, 400) + diff_flat_n.data_type = "RGBA" diff_flat_n.blend_type = "MIX" diff_amb_add_n = lighting_eval_g.nodes.new("ShaderNodeVectorMath") @@ -285,11 +286,12 @@ def __create_group__(): spec_mult_n.location = (start_pos_x + pos_x_shift * 8, -200) spec_mult_n.operation = "MULTIPLY" - spec_flat_n = lighting_eval_g.nodes.new("ShaderNodeMixRGB") + spec_flat_n = lighting_eval_g.nodes.new("ShaderNodeMix") spec_flat_n.name = spec_flat_n.label = _SPEC_FLAT_NODE spec_flat_n.location = (start_pos_x + pos_x_shift * 9, -200) + spec_flat_n.data_type = "RGBA" spec_flat_n.blend_type = "MIX" - spec_flat_n.inputs["Color2"].default_value = (0.0,) * 4 + spec_flat_n.inputs["B"].default_value = (0.0,) * 4 # group links # pass #-2 @@ -320,15 +322,15 @@ def __create_group__(): lighting_eval_g.links.new(n_dot_h_n.inputs[1], input_n.outputs["Normal Vector"]) # pass #4 - lighting_eval_g.links.new(diff_flat_n.inputs["Fac"], input_n.outputs["Flat Lighting"]) - lighting_eval_g.links.new(diff_flat_n.inputs["Color1"], diff_mult_n.outputs[0]) - lighting_eval_g.links.new(diff_flat_n.inputs["Color2"], diff_flat_mult_n.outputs[0]) + lighting_eval_g.links.new(diff_flat_n.inputs["Factor"], input_n.outputs["Flat Lighting"]) + lighting_eval_g.links.new(diff_flat_n.inputs["A"], diff_mult_n.outputs[0]) + lighting_eval_g.links.new(diff_flat_n.inputs["B"], diff_flat_mult_n.outputs[0]) lighting_eval_g.links.new(n_dot_h_max_n.inputs[0], n_dot_h_n.outputs["Value"]) # pass #5 lighting_eval_g.links.new(diff_amb_add_n.inputs[0], light_amb_n.outputs["Color"]) - lighting_eval_g.links.new(diff_amb_add_n.inputs[1], diff_flat_n.outputs["Color"]) + lighting_eval_g.links.new(diff_amb_add_n.inputs[1], diff_flat_n.outputs["Result"]) lighting_eval_g.links.new(n_dot_l_spec_cut_n.inputs[0], n_dot_l_n.outputs["Value"]) @@ -344,12 +346,12 @@ def __create_group__(): lighting_eval_g.links.new(spec_mult_n.inputs[1], light_spec_n.outputs["Color"]) # pass #8 - lighting_eval_g.links.new(spec_flat_n.inputs["Fac"], input_n.outputs["Flat Lighting"]) - lighting_eval_g.links.new(spec_flat_n.inputs["Color1"], spec_mult_n.outputs[0]) + lighting_eval_g.links.new(spec_flat_n.inputs["Factor"], input_n.outputs["Flat Lighting"]) + lighting_eval_g.links.new(spec_flat_n.inputs["A"], spec_mult_n.outputs[0]) # pass: out lighting_eval_g.links.new(output_n.inputs["Diffuse Lighting"], diff_amb_add_n.outputs[0]) - lighting_eval_g.links.new(output_n.inputs["Specular Lighting"], spec_flat_n.outputs["Color"]) + lighting_eval_g.links.new(output_n.inputs["Specular Lighting"], spec_flat_n.outputs["Result"]) lighting_eval_g.links.new(output_n.inputs["Normal"], input_n.outputs["Normal Vector"]) # set default lighting diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/linear_to_srgb_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/linear_to_srgb_ng.py index 06b16f40..8bead632 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/linear_to_srgb_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/linear_to_srgb_ng.py @@ -68,18 +68,20 @@ def __create_linear_to_srgb_group__(): start_pos_x = 0 pos_x_shift = 185 - # outputs defining # inputs defining - lin_to_srgb_g.inputs.new("NodeSocketFloat", "Value") + lin_to_srgb_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Value") + + # outputs defining + lin_to_srgb_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Value") + + + # group nodes input_n = lin_to_srgb_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, 0) - # outputs defining - lin_to_srgb_g.outputs.new("NodeSocketFloat", "Value") output_n = lin_to_srgb_g.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 7, 0) - # group nodes small_mult_n = lin_to_srgb_g.nodes.new("ShaderNodeMath") small_mult_n.name = small_mult_n.label = _SMALL_MULT_NODE small_mult_n.location = (start_pos_x + pos_x_shift * 3, 200) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/mult2_mix_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/mult2_mix_ng.py index a9dbb63d..42ae88af 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/mult2_mix_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/mult2_mix_ng.py @@ -58,21 +58,24 @@ def __create_node_group__(): mult2_mix_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=MULT2_MIX_G) # inputs defining - mult2_mix_g.inputs.new("NodeSocketFloat", "Base Alpha") - mult2_mix_g.inputs.new("NodeSocketColor", "Base Color") - mult2_mix_g.inputs.new("NodeSocketFloat", "Mult Alpha") - mult2_mix_g.inputs.new("NodeSocketColor", "Mult Color") + mult2_mix_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Base Alpha") + mult2_mix_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Base Color") + mult2_mix_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Mult Alpha") + mult2_mix_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Mult Color") + + # outputs defining + mult2_mix_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Mix Alpha") + mult2_mix_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Mix Color") + + + # nodes creation input_n = mult2_mix_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, start_pos_y) - # outputs defining - mult2_mix_g.outputs.new("NodeSocketFloat", "Mix Alpha") - mult2_mix_g.outputs.new("NodeSocketColor", "Mix Color") output_n = mult2_mix_g.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y) - # nodes creation - separate_mult_n = mult2_mix_g.nodes.new("ShaderNodeSeparateRGB") + separate_mult_n = mult2_mix_g.nodes.new("ShaderNodeSeparateColor") separate_mult_n.name = _SEPARATE_MULT_NODE separate_mult_n.label = _SEPARATE_MULT_NODE separate_mult_n.location = (start_pos_x + pos_x_shift, start_pos_y) @@ -84,12 +87,13 @@ def __create_node_group__(): mult_green_scale_n.operation = "MULTIPLY" mult_green_scale_n.inputs[1].default_value = 2.0 - mult_green_mix_n = mult2_mix_g.nodes.new("ShaderNodeMixRGB") + mult_green_mix_n = mult2_mix_g.nodes.new("ShaderNodeMix") mult_green_mix_n.name = _MULT_GREEN_MIX_NODE mult_green_mix_n.label = _MULT_GREEN_MIX_NODE mult_green_mix_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 200) + mult_green_mix_n.data_type = "RGBA" mult_green_mix_n.blend_type = "MIX" - mult_green_mix_n.inputs["Color2"].default_value = (1.0,) * 4 + mult_green_mix_n.inputs["B"].default_value = (1.0,) * 4 mult_base_mult_n = mult2_mix_g.nodes.new("ShaderNodeVectorMath") mult_base_mult_n.name = _MULT_BASE_MULT_NODE @@ -97,25 +101,27 @@ def __create_node_group__(): mult_base_mult_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 400) mult_base_mult_n.operation = "MULTIPLY" - alpha_mix_n = mult2_mix_g.nodes.new("ShaderNodeMixRGB") + alpha_mix_n = mult2_mix_g.nodes.new("ShaderNodeMix") alpha_mix_n.name = _ALPHA_MIX_NODE alpha_mix_n.label = _ALPHA_MIX_NODE alpha_mix_n.location = (start_pos_x + pos_x_shift, start_pos_y - 200) + alpha_mix_n.data_type = "RGBA" + alpha_mix_n.blend_type = "MIX" # links creation - mult2_mix_g.links.new(separate_mult_n.inputs["Image"], input_n.outputs["Mult Color"]) + mult2_mix_g.links.new(separate_mult_n.inputs["Color"], input_n.outputs["Mult Color"]) - mult2_mix_g.links.new(mult_green_scale_n.inputs[0], separate_mult_n.outputs["G"]) + mult2_mix_g.links.new(mult_green_scale_n.inputs[0], separate_mult_n.outputs["Green"]) - mult2_mix_g.links.new(mult_green_mix_n.inputs["Fac"], input_n.outputs["Base Alpha"]) - mult2_mix_g.links.new(mult_green_mix_n.inputs["Color1"], mult_green_scale_n.outputs["Value"]) + mult2_mix_g.links.new(mult_green_mix_n.inputs["Factor"], input_n.outputs["Base Alpha"]) + mult2_mix_g.links.new(mult_green_mix_n.inputs["A"], mult_green_scale_n.outputs["Value"]) mult2_mix_g.links.new(mult_base_mult_n.inputs[0], input_n.outputs["Base Color"]) - mult2_mix_g.links.new(mult_base_mult_n.inputs[1], mult_green_mix_n.outputs["Color"]) + mult2_mix_g.links.new(mult_base_mult_n.inputs[1], mult_green_mix_n.outputs["Result"]) - mult2_mix_g.links.new(alpha_mix_n.inputs["Fac"], input_n.outputs["Base Alpha"]) - mult2_mix_g.links.new(alpha_mix_n.inputs["Color1"], input_n.outputs["Mult Alpha"]) - mult2_mix_g.links.new(alpha_mix_n.inputs["Color2"], input_n.outputs["Base Alpha"]) + mult2_mix_g.links.new(alpha_mix_n.inputs["Factor"], input_n.outputs["Base Alpha"]) + mult2_mix_g.links.new(alpha_mix_n.inputs["A"], input_n.outputs["Mult Alpha"]) + mult2_mix_g.links.new(alpha_mix_n.inputs["B"], input_n.outputs["Base Alpha"]) mult2_mix_g.links.new(output_n.inputs["Mix Color"], mult_base_mult_n.outputs[0]) - mult2_mix_g.links.new(output_n.inputs["Mix Alpha"], alpha_mix_n.outputs["Color"]) + mult2_mix_g.links.new(output_n.inputs["Mix Alpha"], alpha_mix_n.outputs["Result"]) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/refl_normal_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/refl_normal_ng.py index 15215bb4..51ca5205 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/refl_normal_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/refl_normal_ng.py @@ -47,17 +47,20 @@ def __create_refl_normal_group__(): refl_normal_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=REFL_NORMAL_G) # inputs defining - refl_normal_g.inputs.new("NodeSocketVector", "Incoming") - refl_normal_g.inputs.new("NodeSocketVector", "Normal") + refl_normal_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Incoming") + refl_normal_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Normal") + + # outputs defining + refl_normal_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Reflection Normal") + + + # group nodes input_n = refl_normal_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - refl_normal_g.outputs.new("NodeSocketVector", "Reflection Normal") output_n = refl_normal_g.nodes.new("NodeGroupOutput") output_n.location = (185 * 7, 0) - # group nodes view_vector_n = refl_normal_g.nodes.new("ShaderNodeVectorMath") view_vector_n.location = (185, 250) view_vector_n.operation = "MULTIPLY" diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_combine_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_combine_ng.py index d71f5410..6d997d28 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_combine_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_combine_ng.py @@ -49,17 +49,20 @@ def __create_node_group__(): scs_uvs_combine_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=SCS_UVS_COMBINE_G) # inputs defining - scs_uvs_combine_g.inputs.new("NodeSocketFloat", "U") - scs_uvs_combine_g.inputs.new("NodeSocketFloat", "V") + scs_uvs_combine_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "U") + scs_uvs_combine_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "V") + + # outputs defining + scs_uvs_combine_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Vector") + + + # group nodes input_n = scs_uvs_combine_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - scs_uvs_combine_g.outputs.new("NodeSocketVector", "Vector") output_n = scs_uvs_combine_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 4, 0) - # group nodes v_to_scs_inv_n = scs_uvs_combine_g.nodes.new("ShaderNodeMath") v_to_scs_inv_n.location = (pos_x_shift * 1, -100) v_to_scs_inv_n.operation = "MULTIPLY" diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_separate_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_separate_ng.py index 0bc4f906..9656836f 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_separate_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/scs_uvs_separate_ng.py @@ -49,17 +49,20 @@ def __create_node_group__(): scs_uvs_separate_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=SCS_UVS_SEPARATE_G) # inputs defining - scs_uvs_separate_g.inputs.new("NodeSocketVector", "UV") + scs_uvs_separate_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "UV") + + # outputs defining + scs_uvs_separate_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "U") + scs_uvs_separate_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "V") + + + # group nodes input_n = scs_uvs_separate_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - scs_uvs_separate_g.outputs.new("NodeSocketFloat", "U") - scs_uvs_separate_g.outputs.new("NodeSocketFloat", "V") output_n = scs_uvs_separate_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 4, 0) - # group nodes separate_xyz_n = scs_uvs_separate_g.nodes.new("ShaderNodeSeparateXYZ") separate_xyz_n.location = (pos_x_shift * 1, 0) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/spec_texture_calc_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/spec_texture_calc_ng.py new file mode 100644 index 00000000..b521da6d --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/spec_texture_calc_ng.py @@ -0,0 +1,83 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2024: SCS Software + +import bpy +from io_scs_tools.consts import Material as _MAT_consts + +SPEC_TEXTURE_CALC_G = _MAT_consts.node_group_prefix + "SpecularTextureCalc" + +_COLOR_TO_RGB_NODE = "ColorToRGB" +_SHININESS_MULT = "ShininessMult" + + +def get_node_group(): + """Gets node group for specular and shininess calculation. + + :return: node group + :rtype: bpy.types.NodeGroup + """ + + if SPEC_TEXTURE_CALC_G not in bpy.data.node_groups: + __create_node_group__() + + return bpy.data.node_groups[SPEC_TEXTURE_CALC_G] + +def __create_node_group__(): + """Creates specular and shininess group. + + Inputs: Color + Outputs: Shininess, Specular + """ + + spec_txt_calc_n = bpy.data.node_groups.new(type="ShaderNodeTree", name=SPEC_TEXTURE_CALC_G) + + # inputs defining + spec_txt_calc_n.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Color") + + # outputs defining + spec_txt_calc_n.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Shininess") + spec_txt_calc_n.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Specular") + + + # node creation + input_n = spec_txt_calc_n.nodes.new("NodeGroupInput") + input_n.location = (0, 0) + + output_n = spec_txt_calc_n.nodes.new("NodeGroupOutput") + output_n.location = (185 * 3, 0) + + color_to_rgb_n = spec_txt_calc_n.nodes.new("ShaderNodeSeparateColor") + color_to_rgb_n.name = color_to_rgb_n.label = _COLOR_TO_RGB_NODE + color_to_rgb_n.location = (185, 0) + color_to_rgb_n.mode = "RGB" + + shininess_mult_n = spec_txt_calc_n.nodes.new("ShaderNodeVectorMath") + shininess_mult_n.name = shininess_mult_n.label = _SHININESS_MULT + shininess_mult_n.location = (185 * 2, 100) + shininess_mult_n.operation = "MULTIPLY" + shininess_mult_n.inputs[1].default_value = (255,) * 3 + + # links + spec_txt_calc_n.links.new(input_n.outputs['Color'], color_to_rgb_n.inputs['Color']) + + spec_txt_calc_n.links.new(color_to_rgb_n.outputs['Red'], shininess_mult_n.inputs[0]) + spec_txt_calc_n.links.new(color_to_rgb_n.outputs['Green'], output_n.inputs['Specular']) + + spec_txt_calc_n.links.new(shininess_mult_n.outputs['Vector'], output_n.inputs['Shininess']) \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input_ng.py b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input_ng.py index 2c333cdb..01644f31 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_node_groups/vcolor_input_ng.py @@ -66,12 +66,14 @@ def __create_vcolor_group__(): vcol_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=VCOLOR_G) # outputs defining - vcol_g.outputs.new("NodeSocketColor", "Vertex Color") - vcol_g.outputs.new("NodeSocketFloat", "Vertex Color Alpha") + vcol_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Vertex Color", description = "Vertex color output") + vcol_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Vertex Color Alpha", description = "Vertex color output") + + + # group nodes output_n = vcol_g.nodes.new("NodeGroupOutput") output_n.location = (185 * 5, 0) - # group nodes vcol_n = vcol_g.nodes.new("ShaderNodeVertexColor") vcol_n.name = vcol_n.label = _VCOL_ATTRIBUTE_NODE vcol_n.location = (pos_x_shift, 200) @@ -82,7 +84,7 @@ def __create_vcolor_group__(): vcol_a_n.location = (pos_x_shift, -100) vcol_a_n.layer_name = _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix - vcol_separate_rgb_n = vcol_g.nodes.new("ShaderNodeSeparateRGB") + vcol_separate_rgb_n = vcol_g.nodes.new("ShaderNodeSeparateColor") vcol_separate_rgb_n.name = vcol_separate_rgb_n.label = _VCOL_SEPARATE_NODE vcol_separate_rgb_n.location = (pos_x_shift * 2, 200) @@ -116,24 +118,24 @@ def __create_vcolor_group__(): alpha_extend_n.operation = "MULTIPLY" alpha_extend_n.inputs[1].default_value = 2.0 - vcol_combine_n = vcol_g.nodes.new("ShaderNodeCombineRGB") + vcol_combine_n = vcol_g.nodes.new("ShaderNodeCombineColor") vcol_combine_n.name = vcol_combine_n.label = _VCOL_COMBINE_NODE vcol_combine_n.location = (pos_x_shift * 4, 200) # group links - vcol_g.links.new(vcol_separate_rgb_n.inputs['Image'], vcol_n.outputs['Color']) + vcol_g.links.new(vcol_separate_rgb_n.inputs['Color'], vcol_n.outputs['Color']) vcol_g.links.new(alpha_to_bw_n.inputs["Color"], vcol_a_n.outputs['Color']) - vcol_g.links.new(vcol_r_lin_to_srgb_n.inputs['Value'], vcol_separate_rgb_n.outputs['R']) - vcol_g.links.new(vcol_g_lin_to_srgb_n.inputs['Value'], vcol_separate_rgb_n.outputs['G']) - vcol_g.links.new(vcol_b_lin_to_srgb_n.inputs['Value'], vcol_separate_rgb_n.outputs['B']) + vcol_g.links.new(vcol_r_lin_to_srgb_n.inputs['Value'], vcol_separate_rgb_n.outputs['Red']) + vcol_g.links.new(vcol_g_lin_to_srgb_n.inputs['Value'], vcol_separate_rgb_n.outputs['Green']) + vcol_g.links.new(vcol_b_lin_to_srgb_n.inputs['Value'], vcol_separate_rgb_n.outputs['Blue']) vcol_g.links.new(alpha_lin_to_srgb_n.inputs['Value'], alpha_to_bw_n.outputs['Val']) - vcol_g.links.new(vcol_combine_n.inputs['R'], vcol_r_lin_to_srgb_n.outputs["Value"]) - vcol_g.links.new(vcol_combine_n.inputs['G'], vcol_g_lin_to_srgb_n.outputs["Value"]) - vcol_g.links.new(vcol_combine_n.inputs['B'], vcol_b_lin_to_srgb_n.outputs["Value"]) + vcol_g.links.new(vcol_combine_n.inputs['Red'], vcol_r_lin_to_srgb_n.outputs["Value"]) + vcol_g.links.new(vcol_combine_n.inputs['Green'], vcol_g_lin_to_srgb_n.outputs["Value"]) + vcol_g.links.new(vcol_combine_n.inputs['Blue'], vcol_b_lin_to_srgb_n.outputs["Value"]) vcol_g.links.new(alpha_extend_n.inputs[0], alpha_lin_to_srgb_n.outputs['Value']) - vcol_g.links.new(output_n.inputs['Vertex Color'], vcol_combine_n.outputs['Image']) + vcol_g.links.new(output_n.inputs['Vertex Color'], vcol_combine_n.outputs['Color']) vcol_g.links.new(output_n.inputs['Vertex Color Alpha'], alpha_extend_n.outputs['Value']) diff --git a/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py b/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py index 3834702e..26bf2728 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py +++ b/addon/io_scs_tools/internals/shaders/eut2/std_passes/add_env.py @@ -42,8 +42,8 @@ def add(node_tree, geom_n_name, spec_col_socket, alpha_socket, final_normal_sock :type node_tree: bpy.types.NodeTree :param geom_n_name: name of geometry node from which normal and view vectors will be taken :type geom_n_name: str - :param spec_col_socket: specular color node socket from which specular color will be taken - :type spec_col_socket: bpy.type.NodeSocket + :param spec_col_socket: specular color node socket from which specular color will be taken (if None it won't be used) + :type spec_col_socket: bpy.type.NodeSocket | None :param alpha_socket: socket from which alpha will be taken (if None it won't be used) :type alpha_socket: bpy.type.NodeSocket | None :param final_normal_socket: socket of final normal, if not provided geometry normal is used @@ -105,7 +105,10 @@ def add(node_tree, geom_n_name, spec_col_socket, alpha_socket, final_normal_sock node_tree.links.new(add_env_n.inputs['Env Factor Color'], env_col_n.outputs['Color']) node_tree.links.new(add_env_n.inputs['Reflection Texture Color'], refl_tex_n.outputs['Color']) - node_tree.links.new(add_env_n.inputs['Specular Color'], spec_col_socket) + # fix for 'baked' shader that don't use specular + if spec_col_socket is not None: + node_tree.links.new(add_env_n.inputs['Specular Color'], spec_col_socket) + if add_env_n and alpha_socket: node_tree.links.new(add_env_n.inputs['Base Texture Alpha'], alpha_socket) diff --git a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py index 35e7ef5f..356ba123 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/__init__.py @@ -115,26 +115,29 @@ def init(node_tree): opacity_vcol_n.operation = "MULTIPLY" # node creation - level 4 - paint_spec_mult_n = node_tree.nodes.new("ShaderNodeMixRGB") + paint_spec_mult_n = node_tree.nodes.new("ShaderNodeMix") paint_spec_mult_n.name = paint_spec_mult_n.label = Truckpaint.PAINT_SPECULAR_MULT_NODE paint_spec_mult_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 1950) + paint_spec_mult_n.data_type = "RGBA" paint_spec_mult_n.blend_type = "MULTIPLY" - paint_spec_mult_n.inputs['Fac'].default_value = 0 + paint_spec_mult_n.inputs['Factor'].default_value = 0 # node creation - level 5 - base_paint_mult_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_paint_mult_n = node_tree.nodes.new("ShaderNodeMix") base_paint_mult_n.name = base_paint_mult_n.label = Truckpaint.BASE_PAINT_MULT_NODE base_paint_mult_n.location = (start_pos_x + pos_x_shift * 6, start_pos_y + 1500) + base_paint_mult_n.data_type = "RGBA" base_paint_mult_n.blend_type = "MULTIPLY" - base_paint_mult_n.inputs['Fac'].default_value = 1 - base_paint_mult_n.inputs['Color2'].default_value = _convert_utils.to_node_color(_get_scs_globals().base_paint_color) + base_paint_mult_n.inputs['Factor'].default_value = 1 + base_paint_mult_n.inputs['B'].default_value = _convert_utils.to_node_color(_get_scs_globals().base_paint_color) # node creation - level 6 - paint_diff_mult_n = node_tree.nodes.new("ShaderNodeMixRGB") + paint_diff_mult_n = node_tree.nodes.new("ShaderNodeMix") paint_diff_mult_n.name = paint_diff_mult_n.label = Truckpaint.PAINT_DIFFUSE_MULT_NODE paint_diff_mult_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 1400) + paint_diff_mult_n.data_type = "RGBA" paint_diff_mult_n.blend_type = "MULTIPLY" - paint_diff_mult_n.inputs['Fac'].default_value = 0 + paint_diff_mult_n.inputs['Factor'].default_value = 0 # make links - level 2 node_tree.links.new(env_vcol_mix_n.inputs[0], env_color_n.outputs['Color']) @@ -147,23 +150,23 @@ def init(node_tree): node_tree.links.new(opacity_vcol_n.inputs[1], opacity_n.outputs[0]) # make links - level 3 - node_tree.links.new(paint_spec_mult_n.inputs['Color1'], spec_color_n.outputs['Color']) + node_tree.links.new(paint_spec_mult_n.inputs['A'], spec_color_n.outputs['Color']) # make links - level 4 - node_tree.links.new(spec_mult_n.inputs[0], paint_spec_mult_n.outputs['Color']) + node_tree.links.new(spec_mult_n.inputs[0], paint_spec_mult_n.outputs['Result']) node_tree.links.new(spec_mult_n.inputs[1], spec_vcol_mix_n.outputs[0]) - node_tree.links.new(base_paint_mult_n.inputs['Color1'], diff_mult_n.outputs[0]) + node_tree.links.new(base_paint_mult_n.inputs['A'], diff_mult_n.outputs[0]) # make links - level 5 node_tree.links.new(add_refl_gn.inputs['Env Factor Color'], env_vcol_mix_n.outputs[0]) - node_tree.links.new(add_refl_gn.inputs['Specular Color'], paint_spec_mult_n.outputs['Color']) + node_tree.links.new(add_refl_gn.inputs['Specular Color'], paint_spec_mult_n.outputs['Result']) - node_tree.links.new(paint_diff_mult_n.inputs['Color1'], base_paint_mult_n.outputs['Color']) + node_tree.links.new(paint_diff_mult_n.inputs['A'], base_paint_mult_n.outputs['Result']) # make links - output - # node_tree.links.new(compose_lighting_n.inputs['Specular Color'], paint_spec_mult_n.outputs['Color']) - node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], paint_diff_mult_n.outputs['Color']) + # node_tree.links.new(compose_lighting_n.inputs['Specular Color'], paint_spec_mult_n.outputs['Result']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], paint_diff_mult_n.outputs['Result']) @staticmethod def init_colormask_or_airbrush(node_tree): @@ -180,7 +183,7 @@ def init_colormask_or_airbrush(node_tree): # disable base paint color if Truckpaint.BASE_PAINT_MULT_NODE in node_tree.nodes: - node_tree.nodes[Truckpaint.BASE_PAINT_MULT_NODE].inputs["Fac"].default_value = 0 + node_tree.nodes[Truckpaint.BASE_PAINT_MULT_NODE].inputs["Factor"].default_value = 0 paint_diff_mult_n = node_tree.nodes[Truckpaint.PAINT_DIFFUSE_MULT_NODE] paint_spec_mult_n = node_tree.nodes[Truckpaint.PAINT_SPECULAR_MULT_NODE] @@ -214,68 +217,73 @@ def init_colormask_or_airbrush(node_tree): paint_r_col_n.location = (start_pos_x + pos_x_shift, start_pos_y + 50) # node creation - level 2 - paint_tex_sep = node_tree.nodes.new("ShaderNodeSeparateRGB") + paint_tex_sep = node_tree.nodes.new("ShaderNodeSeparateColor") paint_tex_sep.name = paint_tex_sep.label = Truckpaint.PAINT_TEX_SEP_NODE paint_tex_sep.location = (start_pos_x + pos_x_shift * 3, start_pos_y + 650) # node creation - level 3 - airbrush_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + airbrush_mix_n = node_tree.nodes.new("ShaderNodeMix") airbrush_mix_n.name = airbrush_mix_n.label = Truckpaint.AIRBRUSH_MIX_NODE airbrush_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 1000) + airbrush_mix_n.data_type = "RGBA" airbrush_mix_n.blend_type = "MIX" - col_mask_b_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + col_mask_b_mix_n = node_tree.nodes.new("ShaderNodeMix") col_mask_b_mix_n.name = col_mask_b_mix_n.label = Truckpaint.COL_MASK_B_MIX_NODE col_mask_b_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 600) + col_mask_b_mix_n.data_type = "RGBA" col_mask_b_mix_n.blend_type = "MIX" - col_mask_g_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + col_mask_g_mix_n = node_tree.nodes.new("ShaderNodeMix") col_mask_g_mix_n.name = col_mask_g_mix_n.label = Truckpaint.COL_MASK_G_MIX_NODE col_mask_g_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 400) + col_mask_g_mix_n.data_type = "RGBA" col_mask_g_mix_n.blend_type = "MIX" - col_mask_r_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + col_mask_r_mix_n = node_tree.nodes.new("ShaderNodeMix") col_mask_r_mix_n.name = col_mask_r_mix_n.label = Truckpaint.COL_MASK_R_MIX_NODE col_mask_r_mix_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y + 200) + col_mask_r_mix_n.data_type = "RGBA" col_mask_r_mix_n.blend_type = "MIX" # node creation - level 4 - blend_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + blend_mix_n = node_tree.nodes.new("ShaderNodeMix") blend_mix_n.name = blend_mix_n.label = Truckpaint.BLEND_MIX_NODE blend_mix_n.location = (start_pos_x + pos_x_shift * 5, start_pos_y + 800) - blend_mix_n.inputs['Fac'].default_value = 1.0 + blend_mix_n.inputs['Factor'].default_value = 1.0 + blend_mix_n.data_type = "RGBA" blend_mix_n.blend_type = "MIX" # make links - level 0 node_tree.links.new(paint_tex_n.inputs['Vector'], paint_uv_map_n.outputs['UV']) # make links - level 1 - node_tree.links.new(paint_tex_sep.inputs['Image'], paint_tex_n.outputs['Color']) + node_tree.links.new(paint_tex_sep.inputs['Color'], paint_tex_n.outputs['Color']) # make links - level 2 - node_tree.links.new(airbrush_mix_n.inputs['Fac'], paint_tex_n.outputs['Alpha']) - node_tree.links.new(airbrush_mix_n.inputs['Color1'], paint_base_col_n.outputs['Color']) - node_tree.links.new(airbrush_mix_n.inputs['Color2'], paint_tex_n.outputs['Color']) + node_tree.links.new(airbrush_mix_n.inputs['Factor'], paint_tex_n.outputs['Alpha']) + node_tree.links.new(airbrush_mix_n.inputs['A'], paint_base_col_n.outputs['Color']) + node_tree.links.new(airbrush_mix_n.inputs['B'], paint_tex_n.outputs['Color']) - node_tree.links.new(col_mask_b_mix_n.inputs['Fac'], paint_tex_sep.outputs['B']) - node_tree.links.new(col_mask_b_mix_n.inputs['Color1'], paint_base_col_n.outputs['Color']) - node_tree.links.new(col_mask_b_mix_n.inputs['Color2'], paint_b_col_n.outputs['Color']) + node_tree.links.new(col_mask_b_mix_n.inputs['Factor'], paint_tex_sep.outputs['Blue']) + node_tree.links.new(col_mask_b_mix_n.inputs['A'], paint_base_col_n.outputs['Color']) + node_tree.links.new(col_mask_b_mix_n.inputs['B'], paint_b_col_n.outputs['Color']) - node_tree.links.new(col_mask_g_mix_n.inputs['Fac'], paint_tex_sep.outputs['G']) - node_tree.links.new(col_mask_g_mix_n.inputs['Color1'], col_mask_b_mix_n.outputs['Color']) - node_tree.links.new(col_mask_g_mix_n.inputs['Color2'], paint_g_col_n.outputs['Color']) + node_tree.links.new(col_mask_g_mix_n.inputs['Factor'], paint_tex_sep.outputs['Green']) + node_tree.links.new(col_mask_g_mix_n.inputs['A'], col_mask_b_mix_n.outputs['Result']) + node_tree.links.new(col_mask_g_mix_n.inputs['B'], paint_g_col_n.outputs['Color']) - node_tree.links.new(col_mask_r_mix_n.inputs['Fac'], paint_tex_sep.outputs['R']) - node_tree.links.new(col_mask_r_mix_n.inputs['Color1'], col_mask_g_mix_n.outputs['Color']) - node_tree.links.new(col_mask_r_mix_n.inputs['Color2'], paint_r_col_n.outputs['Color']) + node_tree.links.new(col_mask_r_mix_n.inputs['Factor'], paint_tex_sep.outputs['Red']) + node_tree.links.new(col_mask_r_mix_n.inputs['A'], col_mask_g_mix_n.outputs['Result']) + node_tree.links.new(col_mask_r_mix_n.inputs['B'], paint_r_col_n.outputs['Color']) # make links - level 3 - node_tree.links.new(blend_mix_n.inputs['Color1'], airbrush_mix_n.outputs['Color']) - node_tree.links.new(blend_mix_n.inputs['Color2'], col_mask_r_mix_n.outputs['Color']) + node_tree.links.new(blend_mix_n.inputs['A'], airbrush_mix_n.outputs['Result']) + node_tree.links.new(blend_mix_n.inputs['B'], col_mask_r_mix_n.outputs['Result']) # make links - level 5 - node_tree.links.new(paint_diff_mult_n.inputs['Color2'], blend_mix_n.outputs['Color']) - node_tree.links.new(paint_spec_mult_n.inputs['Color2'], paint_tex_n.outputs['Alpha']) + node_tree.links.new(paint_diff_mult_n.inputs['B'], blend_mix_n.outputs['Result']) + node_tree.links.new(paint_spec_mult_n.inputs['B'], paint_tex_n.outputs['Alpha']) @staticmethod def set_fresnel(node_tree, bias_scale): @@ -305,7 +313,7 @@ def set_base_paint_color(node_tree, color): return color = _convert_utils.to_node_color(color) - node_tree.nodes[Truckpaint.BASE_PAINT_MULT_NODE].inputs["Color2"].default_value = color + node_tree.nodes[Truckpaint.BASE_PAINT_MULT_NODE].inputs["B"].default_value = color @staticmethod def set_paintjob_texture(node_tree, image): diff --git a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/airbrush.py b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/airbrush.py index b8474e81..205fb96b 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/airbrush.py +++ b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/airbrush.py @@ -40,10 +40,10 @@ def init(node_tree): Truckpaint.init_colormask_or_airbrush(node_tree) blend_mix_n = node_tree.nodes[Truckpaint.BLEND_MIX_NODE] - blend_mix_n.inputs['Fac'].default_value = 0.0 + blend_mix_n.inputs['Factor'].default_value = 0.0 paint_diff_mult_n = node_tree.nodes[Truckpaint.PAINT_DIFFUSE_MULT_NODE] - paint_diff_mult_n.inputs['Fac'].default_value = 1.0 + paint_diff_mult_n.inputs['Factor'].default_value = 1.0 paint_spec_mult_n = node_tree.nodes[Truckpaint.PAINT_SPECULAR_MULT_NODE] - paint_spec_mult_n.inputs['Fac'].default_value = 0.0 + paint_spec_mult_n.inputs['Factor'].default_value = 0.0 diff --git a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/colormask.py b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/colormask.py index 31f0aaa6..c547a299 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/truckpaint/colormask.py +++ b/addon/io_scs_tools/internals/shaders/eut2/truckpaint/colormask.py @@ -40,10 +40,10 @@ def init(node_tree): Truckpaint.init_colormask_or_airbrush(node_tree) blend_mix_n = node_tree.nodes[Truckpaint.BLEND_MIX_NODE] - blend_mix_n.inputs['Fac'].default_value = 1.0 + blend_mix_n.inputs['Factor'].default_value = 1.0 paint_diff_mult_n = node_tree.nodes[Truckpaint.PAINT_DIFFUSE_MULT_NODE] - paint_diff_mult_n.inputs['Fac'].default_value = 1.0 + paint_diff_mult_n.inputs['Factor'].default_value = 1.0 paint_spec_mult_n = node_tree.nodes[Truckpaint.PAINT_SPECULAR_MULT_NODE] - paint_spec_mult_n.inputs['Fac'].default_value = 1.0 + paint_spec_mult_n.inputs['Factor'].default_value = 1.0 diff --git a/addon/io_scs_tools/internals/shaders/eut2/unlit_tex/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/unlit_tex/__init__.py index d85c820f..83183db9 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/unlit_tex/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/unlit_tex/__init__.py @@ -118,17 +118,16 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + out_shader_n = node_tree.nodes[UnlitTex.OUT_SHADER_NODE] # set proper blend method if alpha_test.is_set(node_tree): - material.blend_method = "CLIP" - material.alpha_threshold = 0.05 + out_shader_n.inputs["Alpha Type"].default_value = 0.0 # add alpha test pass if multiply blend enabled, where alphed pixels shouldn't be multiplied as they are discarded if blend_mult.is_set(node_tree): - out_shader_n = node_tree.nodes[UnlitTex.OUT_SHADER_NODE] - # alpha test pass has to get fully opaque input, thus remove transparency linkage if out_shader_n.inputs['Alpha'].links: node_tree.links.remove(out_shader_n.inputs['Alpha'].links[0]) @@ -140,13 +139,16 @@ def finalize(node_tree, material): alpha_test.add_pass(node_tree, shader_from, alpha_from, shader_to) if blend_add.is_set(node_tree): - material.blend_method = "BLEND" + out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_mult.is_set(node_tree): - material.blend_method = "BLEND" + out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_over.is_set(node_tree): - material.blend_method = "BLEND" + out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" - if material.blend_method == "OPAQUE" and node_tree.nodes[UnlitTex.OUT_SHADER_NODE].inputs['Alpha'].links: + if out_shader_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[UnlitTex.OUT_SHADER_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[UnlitTex.OUT_SHADER_NODE].inputs['Alpha'].links[0]) @staticmethod diff --git a/addon/io_scs_tools/internals/shaders/eut2/unlit_vcol_tex/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/unlit_vcol_tex/__init__.py index a1567f7d..9c216044 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/unlit_vcol_tex/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/unlit_vcol_tex/__init__.py @@ -160,19 +160,18 @@ def finalize(node_tree, material): """ material.use_backface_culling = True - material.blend_method = "OPAQUE" + material.surface_render_method = "DITHERED" + + out_shader_n = node_tree.nodes[UnlitVcolTex.OUT_SHADER_NODE] # set proper blend method if alpha_test.is_set(node_tree): - material.blend_method = "CLIP" - material.alpha_threshold = 0.05 + out_shader_n.inputs["Alpha Type"].default_value = 0.0 # add alpha test pass if: # 1. awhite is enabled, as alpha test pass is called before awhite is aplied # 2. multiply blend enabled, where alphed pixels shouldn't be multiplied as they are discarded if awhite.is_set(node_tree) or blend_mult.is_set(node_tree): - out_shader_n = node_tree.nodes[UnlitVcolTex.OUT_SHADER_NODE] - # alpha test pass has to get fully opaque input, thus remove transparency linkage if out_shader_n.inputs['Alpha'].links: node_tree.links.remove(out_shader_n.inputs['Alpha'].links[0]) @@ -184,13 +183,16 @@ def finalize(node_tree, material): alpha_test.add_pass(node_tree, shader_from, alpha_from, shader_to) if blend_add.is_set(node_tree): - material.blend_method = "BLEND" + out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_mult.is_set(node_tree): - material.blend_method = "BLEND" + out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" if blend_over.is_set(node_tree): - material.blend_method = "BLEND" + out_shader_n.inputs["Alpha Type"].default_value = 1.0 + material.surface_render_method = "BLENDED" - if material.blend_method == "OPAQUE" and node_tree.nodes[UnlitVcolTex.OUT_SHADER_NODE].inputs['Alpha'].links: + if out_shader_n.inputs["Alpha Type"].default_value < 0.0 and node_tree.nodes[UnlitVcolTex.OUT_SHADER_NODE].inputs['Alpha'].links: node_tree.links.remove(node_tree.nodes[UnlitVcolTex.OUT_SHADER_NODE].inputs['Alpha'].links[0]) @staticmethod @@ -463,24 +465,28 @@ def set_fadesheet_flavor(node_tree, switch_on): base1_tex_n.location = (base_tex_n.location.x, base_tex_n.location.y - 300) base1_tex_n.width = 140 - base_base1_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_base1_mix_n = node_tree.nodes.new("ShaderNodeMix") base_base1_mix_n.name = base_base1_mix_n.label = UnlitVcolTex.BASE_BASE1_MIX_NODE - base_base1_mix_n.location = (base_tex_n.location.x + 185 * 2, base_tex_n.location.y - 300) + base_base1_mix_n.location = (base_tex_n.location.x + 185 * 2, base_tex_n.location.y - 250) + base_base1_mix_n.data_type = "RGBA" + base_base1_mix_n.blend_type = "MIX" - base_base1_amix_n = node_tree.nodes.new("ShaderNodeMixRGB") + base_base1_amix_n = node_tree.nodes.new("ShaderNodeMix") base_base1_amix_n.name = base_base1_amix_n.label = UnlitVcolTex.BASE_BASE1_AMIX_NODE base_base1_amix_n.location = (base_tex_n.location.x + 185 * 2, base_tex_n.location.y - 500) + base_base1_amix_n.data_type = "RGBA" + base_base1_amix_n.blend_type = "MIX" # links - node_tree.links.new(base_base1_mix_n.inputs['Color1'], base_tex_n.outputs['Color']) - node_tree.links.new(base_base1_mix_n.inputs['Color2'], base1_tex_n.outputs['Color']) + node_tree.links.new(base_base1_mix_n.inputs['A'], base_tex_n.outputs['Color']) + node_tree.links.new(base_base1_mix_n.inputs['B'], base1_tex_n.outputs['Color']) - node_tree.links.new(base_base1_amix_n.inputs['Color1'], base_tex_n.outputs['Alpha']) - node_tree.links.new(base_base1_amix_n.inputs['Color2'], base1_tex_n.outputs['Alpha']) + node_tree.links.new(base_base1_amix_n.inputs['A'], base_tex_n.outputs['Alpha']) + node_tree.links.new(base_base1_amix_n.inputs['B'], base1_tex_n.outputs['Alpha']) - node_tree.links.new(tex_mult_n.inputs[1], base_base1_mix_n.outputs['Color']) + node_tree.links.new(tex_mult_n.inputs[1], base_base1_mix_n.outputs['Result']) - node_tree.links.new(opacity_n.inputs[0], base_base1_amix_n.outputs['Color']) + node_tree.links.new(opacity_n.inputs[0], base_base1_amix_n.outputs['Result']) # flavor creation uvmap_n.location.x -= 185 @@ -490,8 +496,8 @@ def set_fadesheet_flavor(node_tree, switch_on): uvmap_n.outputs['UV'], base_tex_n.inputs[0], base1_tex_n.inputs[0], - base_base1_mix_n.inputs['Fac'], - base_base1_amix_n.inputs['Fac']) + base_base1_mix_n.inputs['Factor'], + base_base1_amix_n.inputs['Factor']) else: fadesheet.delete(node_tree) diff --git a/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py b/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py index 7ea994b3..cac49aa5 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py +++ b/addon/io_scs_tools/internals/shaders/eut2/water/__init__.py @@ -129,22 +129,25 @@ def init(node_tree): normal_normalize_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 700) normal_normalize_n.operation = "NORMALIZE" - near_horizon_env_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + near_horizon_env_mix_n = node_tree.nodes.new("ShaderNodeMix") near_horizon_env_mix_n.name = near_horizon_env_mix_n.label = Water.NEAR_HORIZON_ENV_MIX_NODE near_horizon_env_mix_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 2100) + near_horizon_env_mix_n.data_type = "RGBA" near_horizon_env_mix_n.blend_type = "MIX" - near_horizon_env_mix_n.inputs['Color2'].default_value = (0.0,) * 4 # far horizon is without env, thus lerp to zero + near_horizon_env_mix_n.inputs['B'].default_value = (0.0,) * 4 # far horizon is without env, thus lerp to zero - near_horizon_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + near_horizon_mix_n = node_tree.nodes.new("ShaderNodeMix") near_horizon_mix_n.name = near_horizon_mix_n.label = Water.NEAR_HORIZON_MIX_NODE near_horizon_mix_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y + 1700) + near_horizon_mix_n.data_type = "RGBA" near_horizon_mix_n.blend_type = "MIX" - normal_scramble_n = node_tree.nodes.new("ShaderNodeMixRGB") + normal_scramble_n = node_tree.nodes.new("ShaderNodeMix") normal_scramble_n.name = normal_scramble_n.label = Water.LAY0_LAY1_NORMAL_SCRAMBLE_NODE normal_scramble_n.location = (start_pos_x + pos_x_shift * 8, start_pos_y + 1200) + normal_scramble_n.data_type = "RGBA" normal_scramble_n.blend_type = "MIX" - normal_scramble_n.inputs['Color1'].default_value = (0.0, 0.0, 1.0, 0.0) # WATER_V_NORMAL + normal_scramble_n.inputs['A'].default_value = (0.0, 0.0, 1.0, 0.0) # WATER_V_NORMAL # links creation # pass 2 @@ -158,23 +161,23 @@ def init(node_tree): node_tree.links.new(horizon_mix_n.inputs[0], vcol_mult_n.outputs[0]) node_tree.links.new(horizon_mix_n.inputs[1], horizon_col_n.outputs['Color']) - node_tree.links.new(normal_scramble_n.inputs['Fac'], mix_factor_n.outputs['Scramble Mix Factor']) - node_tree.links.new(normal_scramble_n.inputs['Color2'], normal_normalize_n.outputs['Vector']) + node_tree.links.new(normal_scramble_n.inputs['Factor'], mix_factor_n.outputs['Scramble Mix Factor']) + node_tree.links.new(normal_scramble_n.inputs['B'], normal_normalize_n.outputs['Vector']) # pass 5 - node_tree.links.new(near_horizon_env_mix_n.inputs['Fac'], mix_factor_n.outputs['Mix Factor']) - node_tree.links.new(near_horizon_env_mix_n.inputs['Color1'], near_mix_n.outputs[0]) + node_tree.links.new(near_horizon_env_mix_n.inputs['Factor'], mix_factor_n.outputs['Mix Factor']) + node_tree.links.new(near_horizon_env_mix_n.inputs['A'], near_mix_n.outputs[0]) - node_tree.links.new(near_horizon_mix_n.inputs['Fac'], mix_factor_n.outputs['Mix Factor']) - node_tree.links.new(near_horizon_mix_n.inputs['Color1'], near_mix_n.outputs[0]) - node_tree.links.new(near_horizon_mix_n.inputs['Color2'], horizon_mix_n.outputs[0]) + node_tree.links.new(near_horizon_mix_n.inputs['Factor'], mix_factor_n.outputs['Mix Factor']) + node_tree.links.new(near_horizon_mix_n.inputs['A'], near_mix_n.outputs[0]) + node_tree.links.new(near_horizon_mix_n.inputs['B'], horizon_mix_n.outputs[0]) # pass 6 - node_tree.links.new(lighting_eval_n.inputs['Normal Vector'], normal_scramble_n.outputs['Color']) + node_tree.links.new(lighting_eval_n.inputs['Normal Vector'], normal_scramble_n.outputs['Result']) # pass 7 - node_tree.links.new(compose_lighting_n.inputs['Env Color'], near_horizon_env_mix_n.outputs['Color']) - node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], near_horizon_mix_n.outputs['Color']) + node_tree.links.new(compose_lighting_n.inputs['Env Color'], near_horizon_env_mix_n.outputs['Result']) + node_tree.links.new(compose_lighting_n.inputs['Diffuse Color'], near_horizon_mix_n.outputs['Result']) # add environment pass and normal maps StdAddEnv.add(node_tree, @@ -182,7 +185,7 @@ def init(node_tree): node_tree.nodes[Dif.SPEC_COL_NODE].outputs['Color'], None, node_tree.nodes[Water.LIGHTING_EVAL_NODE].outputs['Normal'], - node_tree.nodes[Water.NEAR_HORIZON_ENV_MIX_NODE].inputs['Color1']) + node_tree.nodes[Water.NEAR_HORIZON_ENV_MIX_NODE].inputs['A']) node_tree.nodes[StdAddEnv.ADD_ENV_GROUP_NODE].inputs['Base Texture Alpha'].default_value = 1 # set full reflection strength @@ -325,8 +328,8 @@ def set_aux3(node_tree, aux_property): _LAYER0_NMAP_MAPPING_NODE = Water.LAYER0_NMAP_UID + Water.POSTFIX_MAPPING_NODE layer0_mapping_n = node_tree.nodes[_LAYER0_NMAP_MAPPING_NODE] - layer0_mapping_n.inputs['Scale'].default_value[0] = 1 / aux_property[2]['value'] - layer0_mapping_n.inputs['Scale'].default_value[1] = 1 / aux_property[3]['value'] + layer0_mapping_n.inputs['Scale'].default_value[0] = 1 / aux_property[2]['value'] if aux_property[2]['value'] != 0 else 0.1 + layer0_mapping_n.inputs['Scale'].default_value[1] = 1 / aux_property[3]['value'] if aux_property[3]['value'] != 0 else 0.1 yaw = math.radians(aux_property[0]['value']) water_stream_n = node_tree.nodes[Water.WATER_STREAM_NODE] @@ -346,8 +349,8 @@ def set_aux4(node_tree, aux_property): _LAYER1_NMAP_MAPPING_NODE = Water.LAYER1_NMAP_UID + Water.POSTFIX_MAPPING_NODE layer1_mapping_n = node_tree.nodes[_LAYER1_NMAP_MAPPING_NODE] - layer1_mapping_n.inputs['Scale'].default_value[0] = 1 / aux_property[2]['value'] - layer1_mapping_n.inputs['Scale'].default_value[1] = 1 / aux_property[3]['value'] + layer1_mapping_n.inputs['Scale'].default_value[0] = 1 / aux_property[2]['value'] if aux_property[2]['value'] != 0 else 0.1 + layer1_mapping_n.inputs['Scale'].default_value[1] = 1 / aux_property[3]['value'] if aux_property[3]['value'] != 0 else 0.1 yaw = math.radians(aux_property[0]['value']) water_stream_n = node_tree.nodes[Water.WATER_STREAM_NODE] diff --git a/addon/io_scs_tools/internals/shaders/eut2/water/mix_factor_ng.py b/addon/io_scs_tools/internals/shaders/eut2/water/mix_factor_ng.py index 426ed101..86f641a7 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/water/mix_factor_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/water/mix_factor_ng.py @@ -55,19 +55,22 @@ def __create_group__(): detail_setup_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=MIX_FACTOR_G) # inputs defining - detail_setup_g.inputs.new("NodeSocketFloat", "Near Distance") - detail_setup_g.inputs.new("NodeSocketFloat", "Far Distance") - detail_setup_g.inputs.new("NodeSocketFloat", "Scramble Distance") + detail_setup_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Near Distance") + detail_setup_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Far Distance") + detail_setup_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Scramble Distance") + + # outputs defining + detail_setup_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Mix Factor") + detail_setup_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Scramble Mix Factor") + + + # group nodes input_n = detail_setup_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x, start_pos_y) - # outputs defining - detail_setup_g.outputs.new("NodeSocketFloat", "Mix Factor") - detail_setup_g.outputs.new("NodeSocketFloat", "Scramble Mix Factor") output_n = detail_setup_g.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 7, start_pos_y) - # group nodes camera_data_n = detail_setup_g.nodes.new("ShaderNodeCameraData") camera_data_n.location = (start_pos_x, start_pos_y + 100) diff --git a/addon/io_scs_tools/internals/shaders/eut2/water/water_stream_ng.py b/addon/io_scs_tools/internals/shaders/eut2/water/water_stream_ng.py index 63041c27..cb3e85d8 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/water/water_stream_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/water/water_stream_ng.py @@ -74,20 +74,23 @@ def __create_node_group__(): water_stream_ng = bpy.data.node_groups.new(type="ShaderNodeTree", name=WATER_STREAM_G) # inputs defining - water_stream_ng.inputs.new("NodeSocketVector", "Yaw0") - water_stream_ng.inputs.new("NodeSocketFloat", "Speed0") - water_stream_ng.inputs.new("NodeSocketVector", "Yaw1") - water_stream_ng.inputs.new("NodeSocketFloat", "Speed1") + water_stream_ng.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Yaw0") + water_stream_ng.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Speed0") + water_stream_ng.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Yaw1") + water_stream_ng.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Speed1") + + # outputs defining + water_stream_ng.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Stream0") + water_stream_ng.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Stream1") + + + # node creation input_n = water_stream_ng.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, start_pos_y) - # outputs defining - water_stream_ng.outputs.new("NodeSocketVector", "Stream0") - water_stream_ng.outputs.new("NodeSocketVector", "Stream1") output_n = water_stream_ng.nodes.new("NodeGroupOutput") output_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y) - # node creation stream0_speed_mult_n = water_stream_ng.nodes.new("ShaderNodeVectorMath") stream0_speed_mult_n.name = stream0_speed_mult_n.label = _STREAM0_SPEED_MULT_NODE stream0_speed_mult_n.location = (start_pos_x, start_pos_y + 100) diff --git a/addon/io_scs_tools/internals/shaders/eut2/window/window_final_uv_ng.py b/addon/io_scs_tools/internals/shaders/eut2/window/window_final_uv_ng.py index ae85fbdf..9a5e864c 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/window/window_final_uv_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/window/window_final_uv_ng.py @@ -50,17 +50,20 @@ def __create_node_group__(): final_uv_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=WINDOW_FINAL_UV_G) # inputs defining - final_uv_g.inputs.new("NodeSocketFloat", "UV") - final_uv_g.inputs.new("NodeSocketFloat", "Factor") + final_uv_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "UV") + final_uv_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Factor") + + # outputs defining + final_uv_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Final UV") + + + # group nodes input_n = final_uv_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - final_uv_g.outputs.new("NodeSocketFloat", "Final UV") output_n = final_uv_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 6, 0) - # group nodes # UV_STEPS = floor(uv * 0.5) half_scale_n = final_uv_g.nodes.new("ShaderNodeMath") half_scale_n.location = (pos_x_shift * 1, 150) diff --git a/addon/io_scs_tools/internals/shaders/eut2/window/window_offset_factor_ng.py b/addon/io_scs_tools/internals/shaders/eut2/window/window_offset_factor_ng.py index 1bd24c9c..f15ec365 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/window/window_offset_factor_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/window/window_offset_factor_ng.py @@ -50,17 +50,20 @@ def __create_node_group__(): offset_factor_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=WINDOW_OFFSET_FACTOR_G) # inputs defining - offset_factor_g.inputs.new("NodeSocketFloat", "WndToEye Up") - offset_factor_g.inputs.new("NodeSocketFloat", "WndToEye Direction") + offset_factor_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "WndToEye Up") + offset_factor_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "WndToEye Direction") + + # outputs defining + offset_factor_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Offset Factor") + + + # group nodes input_n = offset_factor_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - offset_factor_g.outputs.new("NodeSocketFloat", "Offset Factor") output_n = offset_factor_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 6, 0) - # group nodes # ANGLE_TO_ROTATION = saturate(acos(WndToEyeUp)) arcos_n = offset_factor_g.nodes.new("ShaderNodeMath") arcos_n.location = (pos_x_shift * 1, 200) diff --git a/addon/io_scs_tools/internals/shaders/eut2/window/window_uv_offset_ng.py b/addon/io_scs_tools/internals/shaders/eut2/window/window_uv_offset_ng.py index 5bb8046c..04e1fd26 100644 --- a/addon/io_scs_tools/internals/shaders/eut2/window/window_uv_offset_ng.py +++ b/addon/io_scs_tools/internals/shaders/eut2/window/window_uv_offset_ng.py @@ -56,18 +56,21 @@ def __create_node_group__(): uv_offset_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=WINDOW_UV_OFFSET_G) # inputs defining - uv_offset_g.inputs.new("NodeSocketVector", "UV") - uv_offset_g.inputs.new("NodeSocketVector", "Normal") - uv_offset_g.inputs.new("NodeSocketVector", "Incoming") + uv_offset_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "UV") + uv_offset_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Normal") + uv_offset_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Incoming") + + # outputs defining + uv_offset_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV Final") + + + # group nodes input_n = uv_offset_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - uv_offset_g.outputs.new("NodeSocketVector", "UV Final") output_n = uv_offset_g.nodes.new("NodeGroupOutput") output_n.location = (pos_x_shift * 12, 0) - # group nodes # pass 1 es_world_up_n = uv_offset_g.nodes.new("ShaderNodeVectorTransform") es_world_up_n.name = es_world_up_n.label = "ESWorldUp" diff --git a/addon/io_scs_tools/internals/shaders/flavors/awhite.py b/addon/io_scs_tools/internals/shaders/flavors/awhite.py index 2ed9f800..a43eefd8 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/awhite.py +++ b/addon/io_scs_tools/internals/shaders/flavors/awhite.py @@ -32,10 +32,11 @@ def __create_node__(node_tree): :return: alpha to white mixing node :rtype: bpy.types.Node """ - awhite_mix_n = node_tree.nodes.new("ShaderNodeMixRGB") + awhite_mix_n = node_tree.nodes.new("ShaderNodeMix") awhite_mix_n.name = awhite_mix_n.label = _AWHITE_MIX_NODE + awhite_mix_n.data_type = "RGBA" awhite_mix_n.blend_type = "MIX" - awhite_mix_n.inputs['Color1'].default_value = (1,) * 4 + awhite_mix_n.inputs['A'].default_value = (1,) * 4 return awhite_mix_n @@ -62,10 +63,10 @@ def init(node_tree, location, mix_factor_from, color_from, color_to): awhite_mix_n.location = location # links creation - node_tree.links.new(awhite_mix_n.inputs['Fac'], mix_factor_from) - node_tree.links.new(awhite_mix_n.inputs['Color2'], color_from) + node_tree.links.new(awhite_mix_n.inputs['Factor'], mix_factor_from) + node_tree.links.new(awhite_mix_n.inputs['B'], color_from) - node_tree.links.new(color_to, awhite_mix_n.outputs['Color']) + node_tree.links.new(color_to, awhite_mix_n.outputs['Result']) # FIXME: move to old system after: https://developer.blender.org/T68406 is resolved flavor_frame = node_tree.nodes.new(type="NodeFrame") @@ -106,6 +107,6 @@ def get_out_socket(node_tree): :rtype: bpy.types.NodeSocket | None """ if is_set(node_tree): - return node_tree.nodes[_AWHITE_MIX_NODE].outputs['Color'] + return node_tree.nodes[_AWHITE_MIX_NODE].outputs['Result'] return None diff --git a/addon/io_scs_tools/internals/shaders/flavors/fadesheet/fadesheet_compute_ng.py b/addon/io_scs_tools/internals/shaders/flavors/fadesheet/fadesheet_compute_ng.py index 868c66fd..d198bddf 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/fadesheet/fadesheet_compute_ng.py +++ b/addon/io_scs_tools/internals/shaders/flavors/fadesheet/fadesheet_compute_ng.py @@ -84,16 +84,17 @@ def __create_node_group__(): fadesheet_compute_g.nodes.clear() # inputs defining - fadesheet_compute_g.inputs.new("NodeSocketFloat", "FPS") - fadesheet_compute_g.inputs.new("NodeSocketFloat", "FramesRow") - fadesheet_compute_g.inputs.new("NodeSocketFloat", "FramesTotal") - fadesheet_compute_g.inputs.new("NodeSocketVector", "FrameSize") - fadesheet_compute_g.inputs.new("NodeSocketVector", "UV") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FPS") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesRow") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesTotal") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "FrameSize") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "UV") # outputs defining - fadesheet_compute_g.outputs.new("NodeSocketVector", "UV0") - fadesheet_compute_g.outputs.new("NodeSocketVector", "UV1") - fadesheet_compute_g.outputs.new("NodeSocketFloat", "FrameBlend") + fadesheet_compute_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV0") + fadesheet_compute_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV1") + fadesheet_compute_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "FrameBlend") + # node creation input_n = fadesheet_compute_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/flavors/flipsheet/flipsheet_compute_ng.py b/addon/io_scs_tools/internals/shaders/flavors/flipsheet/flipsheet_compute_ng.py index e029df72..7593310c 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/flipsheet/flipsheet_compute_ng.py +++ b/addon/io_scs_tools/internals/shaders/flavors/flipsheet/flipsheet_compute_ng.py @@ -83,14 +83,14 @@ def __create_node_group__(): fadesheet_compute_g.nodes.clear() # inputs defining - fadesheet_compute_g.inputs.new("NodeSocketFloat", "FPS") - fadesheet_compute_g.inputs.new("NodeSocketFloat", "FramesRow") - fadesheet_compute_g.inputs.new("NodeSocketFloat", "FramesTotal") - fadesheet_compute_g.inputs.new("NodeSocketVector", "FrameSize") - fadesheet_compute_g.inputs.new("NodeSocketVector", "UV") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FPS") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesRow") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesTotal") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "FrameSize") + fadesheet_compute_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "UV") # outputs defining - fadesheet_compute_g.outputs.new("NodeSocketVector", "UV") + fadesheet_compute_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "UV") # node creation input_n = fadesheet_compute_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py b/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py index 3d5abfb2..cbbe6f6c 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py +++ b/addon/io_scs_tools/internals/shaders/flavors/nmap/__init__.py @@ -68,6 +68,11 @@ def __create_nodes__(node_tree, location=None, normal_to=None, normal_from=None) nmap_tex_n.name = nmap_tex_n.label = NMAP_TEX_NODE nmap_tex_n.width = 140 + nmap_dds16_n = node_tree.nodes.new("ShaderNodeGroup") + nmap_dds16_n.parent = frame + nmap_dds16_n.name = nmap_dds16_n.label = NMAP_DDS16_GNODE + nmap_dds16_n.node_tree = dds16_ng.get_node_group() + nmap_n = node_tree.nodes.new("ShaderNodeNormalMap") nmap_n.parent = frame nmap_n.name = nmap_n.label = NMAP_NODE @@ -81,8 +86,9 @@ def __create_nodes__(node_tree, location=None, normal_to=None, normal_from=None) # position nodes if location: - nmap_uvs_n.location = (location[0] - 185 * 3, location[1]) - nmap_tex_n.location = (location[0] - 185 * 2, location[1]) + nmap_uvs_n.location = (location[0] - 185 * 4, location[1]) + nmap_tex_n.location = (location[0] - 185 * 3, location[1]) + nmap_dds16_n.location = (location[0] - 185 * 2, location[1] - 220) nmap_n.location = (location[0] - 185, location[1] - 200) nmap_scale_n.location = (location[0], location[1]) @@ -91,58 +97,21 @@ def __create_nodes__(node_tree, location=None, normal_to=None, normal_from=None) node_tree.links.new(nodes[NMAP_UVMAP_NODE].outputs["UV"], nodes[NMAP_TEX_NODE].inputs["Vector"]) - node_tree.links.new(nodes[NMAP_NODE].inputs["Color"], nodes[NMAP_TEX_NODE].outputs["Color"]) + node_tree.links.new(nodes[NMAP_TEX_NODE].outputs["Color"], nodes[NMAP_DDS16_GNODE].inputs["Color"]) + + node_tree.links.new(nodes[NMAP_DDS16_GNODE].outputs["Color"], nodes[NMAP_NODE].inputs["Color"]) + # Commented because default 1.0 strength gives better visualization. There must be something wrong with calculating strenght? (R channel?) + # Probably SCS changed formula a little bit when they abandoned 3-channel normal maps to 2-channel ones. + # node_tree.links.new(nodes[NMAP_DDS16_GNODE].outputs["Strength"], nodes[NMAP_NODE].inputs["Strength"]) + + node_tree.links.new(nodes[NMAP_NODE].outputs["Normal"], nodes[NMAP_SCALE_GNODE].inputs["Modified Normal"]) - node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["NMap Tex Color"], nodes[NMAP_TEX_NODE].outputs["Color"]) if normal_from: - node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["Original Normal"], normal_from) - node_tree.links.new(nodes[NMAP_SCALE_GNODE].inputs["Modified Normal"], nodes[NMAP_NODE].outputs["Normal"]) + node_tree.links.new(normal_from, nodes[NMAP_SCALE_GNODE].inputs["Original Normal"]) # set normal only if we know where to if normal_to: - node_tree.links.new(normal_to, nodes[NMAP_SCALE_GNODE].outputs["Normal"]) - - -def __check_and_create_dds16_node__(node_tree, image): - """Checks if given texture is composed '16-bit DDS' texture and properly create extra node for it's representation. - On the contrary if texture is not 16-bit DDS and node exists clean that node and restore old connections. - - :param node_tree: node tree on which normal map will be used - :type node_tree: bpy.types.NodeTree - :param image: texture image which should be assigned to nmap texture node - :type image: bpy.types.Image - """ - - # in case of DDS simulating 16-bit normal maps create it's group and properly connect it, - # on the other hand if group exists but shouldn't delete group and restore old connections - - is_dds16 = image and image.filepath.endswith(".dds") and image.pixels[2] == 0.0 - if is_dds16 and NMAP_DDS16_GNODE not in node_tree.nodes: - - nmap_dds16_n = node_tree.nodes.new("ShaderNodeGroup") - nmap_dds16_n.parent = node_tree.nodes[NMAP_FLAVOR_FRAME_NODE] - nmap_dds16_n.name = nmap_dds16_n.label = NMAP_DDS16_GNODE - nmap_dds16_n.node_tree = dds16_ng.get_node_group() - - location = node_tree.nodes[NMAP_NODE].location - - node_tree.nodes[NMAP_TEX_NODE].location[0] -= 185 - node_tree.nodes[NMAP_UVMAP_NODE].location[0] -= 185 - nmap_dds16_n.location = (location[0] - 185, location[1]) - - node_tree.links.new(node_tree.nodes[NMAP_DDS16_GNODE].inputs["Color"], node_tree.nodes[NMAP_TEX_NODE].outputs["Color"]) - - node_tree.links.new(node_tree.nodes[NMAP_NODE].inputs["Strength"], node_tree.nodes[NMAP_DDS16_GNODE].outputs["Strength"]) - node_tree.links.new(node_tree.nodes[NMAP_NODE].inputs["Color"], node_tree.nodes[NMAP_DDS16_GNODE].outputs["Color"]) - - elif not is_dds16 and NMAP_DDS16_GNODE in node_tree.nodes: - - node_tree.nodes.remove(node_tree.nodes[NMAP_DDS16_GNODE]) - - node_tree.nodes[NMAP_TEX_NODE].location[0] += 185 - node_tree.nodes[NMAP_UVMAP_NODE].location[0] += 185 - - node_tree.links.new(node_tree.nodes[NMAP_NODE].inputs["Color"], node_tree.nodes[NMAP_TEX_NODE].outputs["Color"]) + node_tree.links.new(nodes[NMAP_SCALE_GNODE].outputs["Normal"], normal_to) def init(node_tree, location, normal_to, normal_from): @@ -184,9 +153,6 @@ def set_texture(node_tree, image): if NMAP_FLAVOR_FRAME_NODE not in node_tree.nodes: __create_nodes__(node_tree) - # in case of DDS simulating 16-bit normal maps create it's group and properly connect it - __check_and_create_dds16_node__(node_tree, image) - # assign texture to texture node first node_tree.nodes[NMAP_TEX_NODE].image = image @@ -237,7 +203,6 @@ def delete(node_tree, preserve_node=False): if NMAP_NODE in node_tree.nodes and not preserve_node: node_tree.nodes.remove(node_tree.nodes[NMAP_TEX_NODE]) node_tree.nodes.remove(node_tree.nodes[NMAP_NODE]) - if NMAP_DDS16_GNODE in node_tree.nodes: - node_tree.nodes.remove(node_tree.nodes[NMAP_DDS16_GNODE]) + node_tree.nodes.remove(node_tree.nodes[NMAP_DDS16_GNODE]) node_tree.nodes.remove(node_tree.nodes[NMAP_SCALE_GNODE]) node_tree.nodes.remove(node_tree.nodes[NMAP_FLAVOR_FRAME_NODE]) diff --git a/addon/io_scs_tools/internals/shaders/flavors/nmap/dds16_ng.py b/addon/io_scs_tools/internals/shaders/flavors/nmap/dds16_ng.py index 801e6749..06aebaf4 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/nmap/dds16_ng.py +++ b/addon/io_scs_tools/internals/shaders/flavors/nmap/dds16_ng.py @@ -59,18 +59,21 @@ def __create_node_group__(): nmap_dds16_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=TSNMAP_DDS16_G) # inputs defining - nmap_dds16_g.inputs.new("NodeSocketColor", "Color") + nmap_dds16_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Color") + + # outputs defining + nmap_dds16_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Strength") + nmap_dds16_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketColor", name = "Color") + + + # group nodes input_n = nmap_dds16_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - nmap_dds16_g.outputs.new("NodeSocketFloat", "Strength") - nmap_dds16_g.outputs.new("NodeSocketColor", "Color") output_n = nmap_dds16_g.nodes.new("NodeGroupOutput") output_n.location = (185 * 7, 0) - # group nodes - separate_rgb_n = nmap_dds16_g.nodes.new("ShaderNodeSeparateRGB") + separate_rgb_n = nmap_dds16_g.nodes.new("ShaderNodeSeparateColor") separate_rgb_n.name = separate_rgb_n.label = _NMAP_TEX_SEP_NODE separate_rgb_n.location = (185 * 1, 100) @@ -108,16 +111,16 @@ def __create_node_group__(): square_pow_n.inputs[1].default_value = 0.5 # 5. pass - combine_rgb_n = nmap_dds16_g.nodes.new("ShaderNodeCombineRGB") + combine_rgb_n = nmap_dds16_g.nodes.new("ShaderNodeCombineColor") combine_rgb_n.name = combine_rgb_n.label = _NMAP_TEX_COMBINE_NODE combine_rgb_n.location = (185 * 6, 100) - combine_rgb_n.inputs['B'].default_value = 1 + combine_rgb_n.inputs['Blue'].default_value = 1 # group links - nmap_dds16_g.links.new(separate_rgb_n.inputs['Image'], input_n.outputs['Color']) + nmap_dds16_g.links.new(separate_rgb_n.inputs['Color'], input_n.outputs['Color']) - nmap_dds16_g.links.new(red_pow_n.inputs[0], separate_rgb_n.outputs['R']) - nmap_dds16_g.links.new(green_pow_n.inputs[0], separate_rgb_n.outputs['G']) + nmap_dds16_g.links.new(red_pow_n.inputs[0], separate_rgb_n.outputs['Red']) + nmap_dds16_g.links.new(green_pow_n.inputs[0], separate_rgb_n.outputs['Green']) nmap_dds16_g.links.new(red_sub_n.inputs[1], red_pow_n.outputs[0]) @@ -126,8 +129,8 @@ def __create_node_group__(): nmap_dds16_g.links.new(square_pow_n.inputs[0], green_sub_n.outputs[0]) - nmap_dds16_g.links.new(combine_rgb_n.inputs['R'], separate_rgb_n.outputs['R']) - nmap_dds16_g.links.new(combine_rgb_n.inputs['G'], separate_rgb_n.outputs['G']) + nmap_dds16_g.links.new(combine_rgb_n.inputs['Red'], separate_rgb_n.outputs['Red']) + nmap_dds16_g.links.new(combine_rgb_n.inputs['Green'], separate_rgb_n.outputs['Green']) - nmap_dds16_g.links.new(output_n.inputs['Color'], combine_rgb_n.outputs['Image']) + nmap_dds16_g.links.new(output_n.inputs['Color'], combine_rgb_n.outputs['Color']) nmap_dds16_g.links.new(output_n.inputs['Strength'], square_pow_n.outputs[0]) diff --git a/addon/io_scs_tools/internals/shaders/flavors/nmap/scale_ng.py b/addon/io_scs_tools/internals/shaders/flavors/nmap/scale_ng.py index 17520e27..40deb3e3 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/nmap/scale_ng.py +++ b/addon/io_scs_tools/internals/shaders/flavors/nmap/scale_ng.py @@ -72,19 +72,22 @@ def __create_nmap_scale_group__(): nmap_scale_g = bpy.data.node_groups.new(type="ShaderNodeTree", name=TSNMAP_SCALE_G) # inputs defining - nmap_scale_g.inputs.new("NodeSocketColor", "NMap Tex Color") - nmap_scale_g.inputs.new("NodeSocketVector", "Original Normal") - nmap_scale_g.inputs.new("NodeSocketVector", "Modified Normal") + nmap_scale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "NMap Tex Color") + nmap_scale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Original Normal") + nmap_scale_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketVector", name = "Modified Normal") + + # outputs defining + nmap_scale_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketVector", name = "Normal") + + + # group nodes input_n = nmap_scale_g.nodes.new("NodeGroupInput") input_n.location = (0, 0) - # outputs defining - nmap_scale_g.outputs.new("NodeSocketVector", "Normal") output_n = nmap_scale_g.nodes.new("NodeGroupOutput") output_n.location = (185 * 7, 0) - # group nodes - separate_rgb_n = nmap_scale_g.nodes.new("ShaderNodeSeparateRGB") + separate_rgb_n = nmap_scale_g.nodes.new("ShaderNodeSeparateColor") separate_rgb_n.name = separate_rgb_n.label = _NMAP_TEX_SEP_NODE separate_rgb_n.location = (185 * 1, 400) @@ -124,21 +127,23 @@ def __create_nmap_scale_group__(): green_math_abs_n.operation = "ABSOLUTE" green_math_abs_n.use_clamp = True - red_mix_n = nmap_scale_g.nodes.new("ShaderNodeMixRGB") + red_mix_n = nmap_scale_g.nodes.new("ShaderNodeMix") red_mix_n.name = red_mix_n.label = _COMBINE_RED_NODE red_mix_n.location = (185 * 5, 100) + red_mix_n.data_type = "RGBA" red_mix_n.blend_type = "MIX" - green_mix_n = nmap_scale_g.nodes.new("ShaderNodeMixRGB") + green_mix_n = nmap_scale_g.nodes.new("ShaderNodeMix") green_mix_n.name = green_mix_n.label = _COMBINE_GREEN_NODE - green_mix_n.location = (185 * 6, -100) + green_mix_n.location = (185 * 6, 0) + green_mix_n.data_type = "RGBA" green_mix_n.blend_type = "MIX" # group links - nmap_scale_g.links.new(separate_rgb_n.inputs['Image'], input_n.outputs['NMap Tex Color']) + nmap_scale_g.links.new(separate_rgb_n.inputs['Color'], input_n.outputs['NMap Tex Color']) - nmap_scale_g.links.new(red_math_sub_n.inputs[0], separate_rgb_n.outputs['R']) - nmap_scale_g.links.new(green_math_sub_n.inputs[0], separate_rgb_n.outputs['G']) + nmap_scale_g.links.new(red_math_sub_n.inputs[0], separate_rgb_n.outputs['Red']) + nmap_scale_g.links.new(green_math_sub_n.inputs[0], separate_rgb_n.outputs['Green']) nmap_scale_g.links.new(red_math_mult_n.inputs[0], red_math_sub_n.outputs[0]) nmap_scale_g.links.new(green_math_mult_n.inputs[0], green_math_sub_n.outputs[0]) @@ -146,12 +151,12 @@ def __create_nmap_scale_group__(): nmap_scale_g.links.new(red_math_abs_n.inputs[0], red_math_mult_n.outputs[0]) nmap_scale_g.links.new(green_math_abs_n.inputs[0], green_math_mult_n.outputs[0]) - nmap_scale_g.links.new(red_mix_n.inputs['Fac'], red_math_abs_n.outputs[0]) - nmap_scale_g.links.new(red_mix_n.inputs['Color1'], input_n.outputs['Original Normal']) - nmap_scale_g.links.new(red_mix_n.inputs['Color2'], input_n.outputs['Modified Normal']) + nmap_scale_g.links.new(red_mix_n.inputs['Factor'], red_math_abs_n.outputs[0]) + nmap_scale_g.links.new(red_mix_n.inputs['A'], input_n.outputs['Original Normal']) + nmap_scale_g.links.new(red_mix_n.inputs['B'], input_n.outputs['Modified Normal']) - nmap_scale_g.links.new(green_mix_n.inputs['Fac'], green_math_abs_n.outputs[0]) - nmap_scale_g.links.new(green_mix_n.inputs['Color1'], red_mix_n.outputs['Color']) - nmap_scale_g.links.new(green_mix_n.inputs['Color2'], input_n.outputs['Modified Normal']) + nmap_scale_g.links.new(green_mix_n.inputs['Factor'], green_math_abs_n.outputs[0]) + nmap_scale_g.links.new(green_mix_n.inputs['A'], red_mix_n.outputs['Result']) + nmap_scale_g.links.new(green_mix_n.inputs['B'], input_n.outputs['Modified Normal']) - nmap_scale_g.links.new(output_n.inputs['Normal'], green_mix_n.outputs['Color']) + nmap_scale_g.links.new(output_n.inputs['Normal'], green_mix_n.outputs['Result']) diff --git a/addon/io_scs_tools/internals/shaders/flavors/sky_back.py b/addon/io_scs_tools/internals/shaders/flavors/nocull.py similarity index 94% rename from addon/io_scs_tools/internals/shaders/flavors/sky_back.py rename to addon/io_scs_tools/internals/shaders/flavors/nocull.py index 39fe47f1..51a6afd9 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/sky_back.py +++ b/addon/io_scs_tools/internals/shaders/flavors/nocull.py @@ -16,14 +16,14 @@ # # ##### END GPL LICENSE BLOCK ##### -# Copyright (C) 2021: SCS Software +# Copyright (C) 2025: SCS Software -FLAVOR_ID = "sky_back" +FLAVOR_ID = "nocull" def init(node_tree): - """Initialize sky background flavor. + """Initialize sky bottom flavor. :param node_tree: node tree on which it will be used :type node_tree: bpy.types.NodeTree diff --git a/addon/io_scs_tools/internals/shaders/flavors/oinv.py b/addon/io_scs_tools/internals/shaders/flavors/oinv.py new file mode 100644 index 00000000..9d2d2b2f --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/flavors/oinv.py @@ -0,0 +1,104 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +FLAVOR_ID = "oinv" +INV_OPAC_NODE = "InvertOpacity" + + +def __create_node__(node_tree): + """Create node for oinv node. + + :param node_tree: node tree on which oinv flavor will be used + :type node_tree: bpy.types.NodeTree + """ + inv_opac_n = node_tree.nodes.new("ShaderNodeInvert") + inv_opac_n.name = inv_opac_n.label = INV_OPAC_NODE + inv_opac_n.inputs[0].default_value = 1.0 + + +def init(node_tree, location, opac_from, oinv_to): + """Initialize oinv flavor + + :param node_tree: node tree on which oinv flavor will be used + :type node_tree: bpy.types.NodeTree + :param location: x position in node tree + :type location (int, int) + :param opac_from: node socket from which oinv flavor should get opacity + :type opac_from: bpy.types.NodeSocket + :param oinv_to: node socket to which result of inverted opacity should be send + :type oinv_to: bpy.types.NodeSocket + """ + + if INV_OPAC_NODE not in node_tree.nodes: + __create_node__(node_tree) + + node_tree.nodes[INV_OPAC_NODE].location = location + + # links creation + nodes = node_tree.nodes + + node_tree.links.new(nodes[INV_OPAC_NODE].inputs["Color"], opac_from) + node_tree.links.new(oinv_to, nodes[INV_OPAC_NODE].outputs[0]) + + # FIXME: move to old system after: https://developer.blender.org/T68406 is resolved + flavor_frame = node_tree.nodes.new(type="NodeFrame") + flavor_frame.name = flavor_frame.label = FLAVOR_ID + + +def delete(node_tree): + """Delete oinv flavor nodes from node tree. + + :param node_tree: node tree from which oinv flavor should be deleted + :type node_tree: bpy.types.NodeTree + """ + + if INV_OPAC_NODE in node_tree.nodes: + + out_socket = None + in_socket = None + + for link in node_tree.links: + + if link.to_node == node_tree.nodes[INV_OPAC_NODE]: + out_socket = link.from_socket + + if link.from_node == node_tree.nodes[INV_OPAC_NODE]: + in_socket = link.to_socket + + node_tree.nodes.remove(node_tree.nodes[INV_OPAC_NODE]) + + # if out and in socket were properly recovered recreate link state without oinv flavor + if out_socket and in_socket: + node_tree.links.new(out_socket, in_socket) + + if FLAVOR_ID in node_tree.nodes: + node_tree.nodes.remove(node_tree.nodes[FLAVOR_ID]) + + +def is_set(node_tree): + """Check if flavor is set or not. + + :param node_tree: node tree which should be checked for existance of this flavor + :type node_tree: bpy.types.NodeTree + :return: True if flavor exists; False otherwise + :rtype: bool + """ + return FLAVOR_ID in node_tree.nodes + diff --git a/addon/io_scs_tools/internals/shaders/flavors/opasrc1.py b/addon/io_scs_tools/internals/shaders/flavors/opasrc1.py new file mode 100644 index 00000000..ed19d67c --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/flavors/opasrc1.py @@ -0,0 +1,106 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + +FLAVOR_ID = "opasrc01" +MULT_OPAC_SRC_NODE = "MultOpaqueSource" + + +def __create_node__(node_tree): + """Create node for oinv node. + + :param node_tree: node tree on which oinv flavor will be used + :type node_tree: bpy.types.NodeTree + """ + mult_opac_src_n = node_tree.nodes.new("ShaderNodeMath") + mult_opac_src_n.name = mult_opac_src_n.label = MULT_OPAC_SRC_NODE + mult_opac_src_n.operation = "MULTIPLY" + mult_opac_src_n.inputs[1].default_value = 1.0 + + +def init(node_tree, location, alpha_from, opac_from, alpha_to): + """Initialize oinv flavor + + :param node_tree: node tree on which oinv flavor will be used + :type node_tree: bpy.types.NodeTree + :param location: x position in node tree + :type location (int, int) + :param alpha_from: node socket from which opasrc flavor should get alpha + :type alpha_from: bpy.types.NodeSocket + :param opac_from: node socket from which opasrc flavor should get opacity + :type opac_from: bpy.types.NodeSocket + :param alpha_to: node socket to which result of alpha and opaque mult should be send + :type alpha_to: bpy.types.NodeSocket + """ + + if MULT_OPAC_SRC_NODE not in node_tree.nodes: + __create_node__(node_tree) + + node_tree.nodes[MULT_OPAC_SRC_NODE].location = location + + # links creation + nodes = node_tree.nodes + + node_tree.links.new(alpha_from, nodes[MULT_OPAC_SRC_NODE].inputs[0]) + node_tree.links.new(opac_from, nodes[MULT_OPAC_SRC_NODE].inputs[1]) + + node_tree.links.new(nodes[MULT_OPAC_SRC_NODE].outputs[0], alpha_to) + + # FIXME: move to old system after: https://developer.blender.org/T68406 is resolved + flavor_frame = node_tree.nodes.new(type="NodeFrame") + flavor_frame.name = flavor_frame.label = FLAVOR_ID + +def delete(node_tree): + """Delete oinv flavor nodes from node tree. + + :param node_tree: node tree from which opasrc flavor should be deleted + :type node_tree: bpy.types.NodeTree + """ + + if MULT_OPAC_SRC_NODE in node_tree.nodes: + + out_socket = None + in_socket = None + + for link in node_tree.links: + + if link.to_node == node_tree.nodes[MULT_OPAC_SRC_NODE]: + out_socket = link.from_socket + + if link.from_node == node_tree.nodes[MULT_OPAC_SRC_NODE]: + in_socket = link.to_socket + + node_tree.nodes.remove(node_tree.nodes[MULT_OPAC_SRC_NODE]) + + # if out and in socket were properly recovered recreate link state without opasrc flavor + if out_socket and in_socket: + node_tree.links.new(out_socket, in_socket) + + if FLAVOR_ID in node_tree.nodes: + node_tree.nodes.remove(node_tree.nodes[FLAVOR_ID]) + +def is_set(node_tree): + """Check if flavor is set or not. + + :param node_tree: node tree which should be checked for existance of this flavor + :type node_tree: bpy.types.NodeTree + :return: True if flavor exists; False otherwise + :rtype: bool + """ + return FLAVOR_ID in node_tree.nodes \ No newline at end of file diff --git a/addon/io_scs_tools/internals/shaders/flavors/sky_bottom.py b/addon/io_scs_tools/internals/shaders/flavors/sky_bottom.py new file mode 100644 index 00000000..3e3a9cfc --- /dev/null +++ b/addon/io_scs_tools/internals/shaders/flavors/sky_bottom.py @@ -0,0 +1,56 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# Copyright (C) 2025: SCS Software + + +FLAVOR_ID = "sky_bottom" + + +def init(node_tree): + """Initialize sky bottom flavor. + + :param node_tree: node tree on which it will be used + :type node_tree: bpy.types.NodeTree + """ + + # FIXME: move to old system after: https://developer.blender.org/T68406 is resolved + flavor_frame = node_tree.nodes.new(type="NodeFrame") + flavor_frame.name = flavor_frame.label = FLAVOR_ID + + +def delete(node_tree): + """Delete flavor from node tree. + + :param node_tree: node tree from which flavor should be deleted + :type node_tree: bpy.types.NodeTree + """ + + if FLAVOR_ID in node_tree.nodes: + node_tree.nodes.remove(node_tree.nodes[FLAVOR_ID]) + + +def is_set(node_tree): + """Check if flavor is set or not. + + :param node_tree: node tree which should be checked for existance of this flavor + :type node_tree: bpy.types.NodeTree + :return: True if flavor exists; False otherwise + :rtype: bool + """ + return FLAVOR_ID in node_tree.nodes diff --git a/addon/io_scs_tools/internals/shaders/flavors/tg0.py b/addon/io_scs_tools/internals/shaders/flavors/tg0.py index c0c2f836..d60248b3 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/tg0.py +++ b/addon/io_scs_tools/internals/shaders/flavors/tg0.py @@ -101,8 +101,8 @@ def is_set(node_tree): return FLAVOR_ID in node_tree.nodes -def set_scale(node_tree, scale_x, scale_y): - """Set scale of tex generation. +def set_scale(node_tree, scale_x, scale_y, rotation): + """Set scale and rotation of tex generation. :param node_tree: node tree which should be checked for existance of this flavor :type node_tree: bpy.types.NodeTree @@ -110,10 +110,13 @@ def set_scale(node_tree, scale_x, scale_y): :type scale_x: float :param scale_y: y coordinate scaling :type scale_y: float + :param rotation: base texture rotation + :type rotation: float """ vector_mapping_n = get_node(node_tree) if vector_mapping_n: + vector_mapping_n.inputs['Rotation'].default_value[2] = rotation / 57.3 vector_mapping_n.inputs['Scale'].default_value[0] = 1 / scale_x vector_mapping_n.inputs['Scale'].default_value[1] = 1 / scale_y diff --git a/addon/io_scs_tools/internals/shaders/flavors/tg1.py b/addon/io_scs_tools/internals/shaders/flavors/tg1.py index 2adae8d7..3306c160 100644 --- a/addon/io_scs_tools/internals/shaders/flavors/tg1.py +++ b/addon/io_scs_tools/internals/shaders/flavors/tg1.py @@ -101,8 +101,8 @@ def is_set(node_tree): return FLAVOR_ID in node_tree.nodes -def set_scale(node_tree, scale_x, scale_y): - """Set scale of tex generation. +def set_scale(node_tree, scale_x, scale_y, rotation): + """Set scale and rotation of tex generation. :param node_tree: node tree which should be checked for existance of this flavor :type node_tree: bpy.types.NodeTree @@ -110,10 +110,13 @@ def set_scale(node_tree, scale_x, scale_y): :type scale_x: float :param scale_y: y coordinate scaling :type scale_y: float + :param rotation: base texture rotation + :type rotation: float """ vector_mapping_n = get_node(node_tree) if vector_mapping_n: + vector_mapping_n.inputs['Rotation'].default_value[2] = rotation / 57.3 vector_mapping_n.inputs['Scale'].default_value[0] = 1 / scale_x vector_mapping_n.inputs['Scale'].default_value[1] = 1 / scale_y diff --git a/addon/io_scs_tools/internals/shaders/shader.py b/addon/io_scs_tools/internals/shaders/shader.py index 9e1c7136..259453f9 100644 --- a/addon/io_scs_tools/internals/shaders/shader.py +++ b/addon/io_scs_tools/internals/shaders/shader.py @@ -62,13 +62,13 @@ def setup_nodes(material, effect, attr_dict, tex_dict, tex_settings_dict, recrea if effect.endswith(".tsnmapuv") or ".tsnmapuv." in effect: flavors["nmap"] = True - if effect.endswith(".tsnmapuv16") or ".tsnmapuv16." in effect: - flavors["nmap"] = True + if effect.endswith(".tsnmapuv2") or ".tsnmapuv2." in effect: + flavors["nmap2"] = True if effect.endswith(".tsnmap") or ".tsnmap." in effect: flavors["nmap"] = True - if effect.endswith(".tsnmap16") or ".tsnmap16." in effect: + if effect.endswith(".tsnmapcalc") or ".tsnmapcalc." in effect: flavors["nmap"] = True if effect.endswith(".indenv") or ".indenv." in effect: @@ -77,6 +77,12 @@ def setup_nodes(material, effect, attr_dict, tex_dict, tex_settings_dict, recrea if effect.endswith(".linv") or ".linv." in effect: flavors["linv"] = True + if effect.endswith(".oinv") or ".oinv." in effect: + flavors["oinv"] = True + + if effect.endswith(".opasrc01") or ".opasrc01." in effect: + flavors["opasrc1"] = True + if effect.endswith(".lvcol") or ".lvcol." in effect: flavors["lvcol"] = True @@ -92,6 +98,9 @@ def setup_nodes(material, effect, attr_dict, tex_dict, tex_settings_dict, recrea if effect.endswith(".paint") or ".paint." in effect: flavors["paint"] = True + if effect.endswith(".lit") or ".lit." in effect: + flavors["lit"] = True + if effect.endswith(".decal.over") and ".retroreflective" in effect: flavors["retroreflective_decal"] = True @@ -107,7 +116,10 @@ def setup_nodes(material, effect, attr_dict, tex_dict, tex_settings_dict, recrea if effect.endswith(".flipsheet") or ".flipsheet." in effect: flavors["flipsheet"] = True - __setup_nodes__(material, effect, attr_dict, tex_dict, tex_settings_dict, {}, flavors, recreate) + if effect.endswith(".nocull") or ".nocull." in effect: + flavors["nocull"] = True + + __setup_nodes__(material, effect, attr_dict, tex_dict, tex_settings_dict, {}, {}, flavors, recreate) def set_attribute(material, attr_type, attr_value): @@ -120,7 +132,7 @@ def set_attribute(material, attr_type, attr_value): :param attr_value: value which should be set to attribute in shader :type attr_value: object """ - __setup_nodes__(material, material.scs_props.mat_effect_name, {attr_type: attr_value}, {}, {}, {}, {}, False) + __setup_nodes__(material, material.scs_props.mat_effect_name, {attr_type: attr_value}, {}, {}, {}, {}, {}, False) def set_texture(material, tex_type, image): @@ -133,7 +145,7 @@ def set_texture(material, tex_type, image): :param image: blender texture image object :type image: bpy.types.Image """ - __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {tex_type: image}, {}, {}, {}, False) + __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {tex_type: image}, {}, {}, {}, {}, False) def set_texture_settings(material, tex_type, settings): @@ -146,7 +158,7 @@ def set_texture_settings(material, tex_type, settings): :param settings: binary string of TOBJ settings gotten from tobj import :type settings: str """ - __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {}, {tex_type: settings}, {}, {}, False) + __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {}, {tex_type: settings}, {}, {}, {}, False) def set_uv(material, tex_type, uv_layer, tex_coord): @@ -185,10 +197,24 @@ def set_uv(material, tex_type, uv_layer, tex_coord): is_valid_input = False if is_valid_input: - __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {}, {}, {tex_type: uv_layer}, {}, False) + __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {}, {}, {tex_type: uv_layer}, {}, {}, False) + +def set_mapping(material, mapping_type, uv_layer, tex_coord): + """Set UV layer to given texture type in material. + + :param material: blender material + :type material: bpy.types.Material + :param mapping_type: type of SCS mapping to set + :type mapping_type: str + :param uv_layer: uv layer name which should be assigned to this texture + :type uv_layer: str + :param tex_coord: index of tex_coord this mapping uses + :type tex_coord: int + """ + __setup_nodes__(material, material.scs_props.mat_effect_name, {}, {}, {}, {}, {mapping_type: uv_layer}, {}, False) -def __setup_nodes__(material, effect, attr_dict, tex_dict, tex_settings_dict, uvs_dict, flavors_dict, recreate): +def __setup_nodes__(material, effect, attr_dict, tex_dict, tex_settings_dict, uvs_dict, mapping_dict, flavors_dict, recreate): """Wrapping setup of nodes for given material in central function. It properly setup nodes for 3D view visualization in real time. @@ -204,6 +230,8 @@ def __setup_nodes__(material, effect, attr_dict, tex_dict, tex_settings_dict, uv :type tex_settings_dict: dict :param uvs_dict: shader uv layers which should be set on given material; entry: (texture_type: string of uv layer) :type uvs_dict: dict + :param mapping_dict: shader uv layers which should be set on given material; entry: (texture_type: string of uv layer) + :type mapping_dict: dict :param flavors_dict: shader flavors which should be set on given material; entry: (flavor_type: flavor_data) :type flavors_dict: dict :param recreate: flag indicating if shader nodes should be recreated. Should be triggered if effect name changes. @@ -262,6 +290,14 @@ def __setup_nodes__(material, effect, attr_dict, tex_dict, tex_settings_dict, uv else: lprint("D Unsupported set_uv with type %r called on shader %r", (tex_type, shader_module.get_name())) + # set mappings + for mapping_type in mapping_dict: + shader_set_mapping = getattr(shader_module, "set_" + mapping_type + "_mapping", None) + if shader_set_mapping: + shader_set_mapping(node_tree, mapping_dict[mapping_type]) + else: + lprint("D Unsupported set_mapping with type %r called on shader %r", (mapping_type, shader_module.get_name())) + # finalize shader on recreate (set material blending method, backface culling etc.) if recreate: shader_module.finalize(node_tree, material) diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/alpha_test_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/alpha_test_ng.py index cd7b95f1..4ded226e 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/alpha_test_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/alpha_test_ng.py @@ -81,11 +81,12 @@ def __create_node_group__(): alpha_test_g.nodes.clear() # inputs defining - alpha_test_g.inputs.new("NodeSocketShader", "Shader") - alpha_test_g.inputs.new("NodeSocketFloat", "Alpha") + alpha_test_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketShader", name = "Shader") + alpha_test_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Alpha") # outputs defining - alpha_test_g.outputs.new("NodeSocketShader", "Shader") + alpha_test_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketShader", name = "Shader") + # node creation input_n = alpha_test_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_idx_to_col_row_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_idx_to_col_row_ng.py index 01ada43e..cea2ca40 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_idx_to_col_row_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_idx_to_col_row_ng.py @@ -84,12 +84,13 @@ def __create_node_group__(): animsheet_frame_idx_to_col_row_g.nodes.clear() # inputs defining - animsheet_frame_idx_to_col_row_g.inputs.new("NodeSocketFloat", "FrameIndex") - animsheet_frame_idx_to_col_row_g.inputs.new("NodeSocketFloat", "FramesRow") + animsheet_frame_idx_to_col_row_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FrameIndex") + animsheet_frame_idx_to_col_row_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesRow") # outputs defining - animsheet_frame_idx_to_col_row_g.outputs.new("NodeSocketFloat", "ColIndex") - animsheet_frame_idx_to_col_row_g.outputs.new("NodeSocketFloat", "RowIndex") + animsheet_frame_idx_to_col_row_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "ColIndex") + animsheet_frame_idx_to_col_row_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "RowIndex") + # node creation input_n = animsheet_frame_idx_to_col_row_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_ng.py index 8ede7502..165b77e5 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_frame_ng.py @@ -84,13 +84,14 @@ def __create_node_group__(): animsheet_xfade_g.nodes.clear() # inputs defining - animsheet_xfade_g.inputs.new("NodeSocketFloat", "FPS") - animsheet_xfade_g.inputs.new("NodeSocketFloat", "FramesTotal") - animsheet_xfade_g.inputs.new("NodeSocketFloat", "FramesRow") + animsheet_xfade_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FPS") + animsheet_xfade_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesTotal") + animsheet_xfade_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesRow") # outputs defining - animsheet_xfade_g.outputs.new("NodeSocketFloat", "FrameX") - animsheet_xfade_g.outputs.new("NodeSocketFloat", "FrameY") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "FrameX") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "FrameY") + # node creation input_n = animsheet_xfade_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_loop_frame_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_loop_frame_ng.py index 3af61979..8e1cf53d 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_loop_frame_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_loop_frame_ng.py @@ -85,11 +85,12 @@ def __create_node_group__(): animsheet_loop_frame_g.nodes.clear() # inputs defining - animsheet_loop_frame_g.inputs.new("NodeSocketFloat", "FPS") - animsheet_loop_frame_g.inputs.new("NodeSocketFloat", "FramesTotal") + animsheet_loop_frame_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FPS") + animsheet_loop_frame_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesTotal") # outputs defining - animsheet_loop_frame_g.outputs.new("NodeSocketFloat", "LoopFrame") + animsheet_loop_frame_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "LoopFrame") + # node creation input_n = animsheet_loop_frame_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_xfade_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_xfade_ng.py index f52b2416..b0a0e293 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_xfade_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/animsheet_xfade_ng.py @@ -89,16 +89,17 @@ def __create_node_group__(): animsheet_xfade_g.nodes.clear() # inputs defining - animsheet_xfade_g.inputs.new("NodeSocketFloat", "FPS") - animsheet_xfade_g.inputs.new("NodeSocketFloat", "FramesTotal") - animsheet_xfade_g.inputs.new("NodeSocketFloat", "FramesRow") + animsheet_xfade_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FPS") + animsheet_xfade_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesTotal") + animsheet_xfade_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "FramesRow") # outputs defining - animsheet_xfade_g.outputs.new("NodeSocketFloat", "Frame0X") - animsheet_xfade_g.outputs.new("NodeSocketFloat", "Frame0Y") - animsheet_xfade_g.outputs.new("NodeSocketFloat", "Frame1X") - animsheet_xfade_g.outputs.new("NodeSocketFloat", "Frame1Y") - animsheet_xfade_g.outputs.new("NodeSocketFloat", "FrameBlend") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Frame0X") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Frame0Y") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Frame1X") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "Frame1Y") + animsheet_xfade_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketFloat", name = "FrameBlend") + # node creation input_n = animsheet_xfade_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/blend_add_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/blend_add_ng.py index b40ec0e1..19ab6ca9 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/blend_add_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/blend_add_ng.py @@ -81,10 +81,11 @@ def __create_node_group__(): blend_add_g.nodes.clear() # inputs defining - blend_add_g.inputs.new("NodeSocketShader", "Shader") + blend_add_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketShader", name = "Shader") # outputs defining - blend_add_g.outputs.new("NodeSocketShader", "Shader") + blend_add_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketShader", name = "Shader") + # node creation input_n = blend_add_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/blend_mult_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/blend_mult_ng.py index 59cdd439..f6947b59 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/blend_mult_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/blend_mult_ng.py @@ -81,10 +81,11 @@ def __create_node_group__(): blend_mult_g.nodes.clear() # inputs defining - blend_mult_g.inputs.new("NodeSocketShader", "Shader") + blend_mult_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketShader", name = "Shader") # outputs defining - blend_mult_g.outputs.new("NodeSocketShader", "Shader") + blend_mult_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketShader", name = "Shader") + # node creation input_n = blend_mult_g.nodes.new("NodeGroupInput") diff --git a/addon/io_scs_tools/internals/shaders/std_node_groups/output_shader_ng.py b/addon/io_scs_tools/internals/shaders/std_node_groups/output_shader_ng.py index c48af937..2de23b4c 100644 --- a/addon/io_scs_tools/internals/shaders/std_node_groups/output_shader_ng.py +++ b/addon/io_scs_tools/internals/shaders/std_node_groups/output_shader_ng.py @@ -24,6 +24,9 @@ OUTPUT_SHADER_G = _MAT_consts.node_group_prefix + "OutputShaderGroup" _ALPHA_CLAMP = "AlphaClamp" +_ALPHA_THRESHOLD = "AlphaThreshold" +_ALPHA_TYPE_SWITCH = "AlphaTypeSwitch" +_ALPHA_TYPE_MIX_SWITCH = "AlphaTypeMixSwitch" _EMISSION_OUT_SHADER = "EmissionMaterial" _TRANSPARENT_OUT_SHADER = "TransparentMaterial" _MIX_SHADER = "MixShader" @@ -85,48 +88,77 @@ def __create_node_group__(): output_shader_g.nodes.clear() # inputs defining - output_shader_g.inputs.new("NodeSocketColor", "Color") - output_shader_g.inputs.new("NodeSocketFloat", "Alpha") + output_shader_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketColor", name = "Color") + output_shader_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Alpha") + output_shader_g.interface.new_socket(in_out = "INPUT", socket_type = "NodeSocketFloat", name = "Alpha Type", description = "0 = CLIP\n1 = BLEND") # always set to full opaque by default since this behaviour is expected by shaders # since this behaviour is epxected by effects from before. - output_shader_g.inputs['Alpha'].default_value = 1 + output_shader_g.interface.items_tree['Alpha'].default_value = 1 + output_shader_g.interface.items_tree['Alpha Type'].default_value = -1 # outputs defining - output_shader_g.outputs.new("NodeSocketShader", "Shader") + output_shader_g.interface.new_socket(in_out = "OUTPUT", socket_type = "NodeSocketShader", name = "Shader") + # node creation input_n = output_shader_g.nodes.new("NodeGroupInput") input_n.location = (start_pos_x - pos_x_shift, start_pos_y) output_n = output_shader_g.nodes.new("NodeGroupOutput") - output_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y) + output_n.location = (start_pos_x + pos_x_shift * 4, start_pos_y) emission_shader_n = output_shader_g.nodes.new("ShaderNodeEmission") emission_shader_n.name = emission_shader_n.label = _EMISSION_OUT_SHADER - emission_shader_n.location = (start_pos_x + pos_x_shift * 1, start_pos_y + 200) + emission_shader_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y + 200) alpha_clamp_n = output_shader_g.nodes.new("ShaderNodeClamp") alpha_clamp_n.name = alpha_clamp_n.label = _ALPHA_CLAMP - alpha_clamp_n.location = (start_pos_x + pos_x_shift * 1, start_pos_y) + alpha_clamp_n.location = (start_pos_x, start_pos_y - 150) alpha_clamp_n.inputs['Min'].default_value = 0.000001 # blender clips if alpha is way above, so it's safe to use it alpha_clamp_n.inputs['Max'].default_value = 1 + alpha_threshold_n = output_shader_g.nodes.new("ShaderNodeMath") + alpha_threshold_n.name = alpha_threshold_n.label = _ALPHA_THRESHOLD + alpha_threshold_n.location = (start_pos_x + pos_x_shift * 1, start_pos_y - 150) + alpha_threshold_n.operation = "GREATER_THAN" + alpha_threshold_n.inputs[1].default_value = 0.05 + + alpha_type_switch_n = output_shader_g.nodes.new("ShaderNodeMath") + alpha_type_switch_n.name = alpha_type_switch_n.label = _ALPHA_TYPE_SWITCH + alpha_type_switch_n.location = (start_pos_x, start_pos_y) + alpha_type_switch_n.operation = "GREATER_THAN" + alpha_type_switch_n.inputs[1].default_value = 0.0 + + alpha_type_mix_switch_n = output_shader_g.nodes.new("ShaderNodeMix") + alpha_type_mix_switch_n.name = alpha_type_mix_switch_n.label = _ALPHA_TYPE_MIX_SWITCH + alpha_type_mix_switch_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y) + alpha_type_mix_switch_n.data_type = "FLOAT" + transparent_shader_n = output_shader_g.nodes.new("ShaderNodeBsdfTransparent") transparent_shader_n.name = transparent_shader_n.label = _TRANSPARENT_OUT_SHADER - transparent_shader_n.location = (start_pos_x + pos_x_shift * 1, start_pos_y - 200) + transparent_shader_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y - 200) mix_shader_n = output_shader_g.nodes.new("ShaderNodeMixShader") mix_shader_n.name = mix_shader_n.label = _MIX_SHADER - mix_shader_n.location = (start_pos_x + pos_x_shift * 2, start_pos_y) + mix_shader_n.location = (start_pos_x + pos_x_shift * 3, start_pos_y) # links # pass 1 - output_shader_g.links.new(emission_shader_n.inputs['Color'], input_n.outputs['Color']) + output_shader_g.links.new(alpha_type_switch_n.inputs['Value'], input_n.outputs['Alpha Type']) output_shader_g.links.new(alpha_clamp_n.inputs['Value'], input_n.outputs['Alpha']) # pass 2 - output_shader_g.links.new(mix_shader_n.inputs[0], alpha_clamp_n.outputs[0]) + output_shader_g.links.new(alpha_threshold_n.inputs['Value'], alpha_clamp_n.outputs['Result']) + + # pass 3 + output_shader_g.links.new(emission_shader_n.inputs['Color'], input_n.outputs['Color']) + output_shader_g.links.new(alpha_type_mix_switch_n.inputs['Factor'], alpha_type_switch_n.outputs['Value']) + output_shader_g.links.new(alpha_type_mix_switch_n.inputs['A'], alpha_threshold_n.outputs['Value']) + output_shader_g.links.new(alpha_type_mix_switch_n.inputs['B'], alpha_clamp_n.outputs['Result']) + + # pass 4 + output_shader_g.links.new(mix_shader_n.inputs[0], alpha_type_mix_switch_n.outputs[0]) output_shader_g.links.new(mix_shader_n.inputs[1], transparent_shader_n.outputs[0]) output_shader_g.links.new(mix_shader_n.inputs[2], emission_shader_n.outputs[0]) diff --git a/addon/io_scs_tools/operators/bases/export.py b/addon/io_scs_tools/operators/bases/export.py index be4ffafb..26d8438a 100644 --- a/addon/io_scs_tools/operators/bases/export.py +++ b/addon/io_scs_tools/operators/bases/export.py @@ -33,7 +33,7 @@ class SCSExportHelper: export_scene_name = None """Stores name of the export scene (used to skip custom drawing elements update on export scene).""" - def __init__(self): + def __init__(self, *args, **kwargs): self.cached_objects = None """:type list[bpy.types.Object]: Used in selection mode to cache selected objects, to avoid multiple collecting.""" self.objects_states = dict() diff --git a/addon/io_scs_tools/operators/material.py b/addon/io_scs_tools/operators/material.py index 06b6037c..4aa543af 100644 --- a/addon/io_scs_tools/operators/material.py +++ b/addon/io_scs_tools/operators/material.py @@ -850,7 +850,8 @@ def execute(self, context): is_aliased_directory = ("/material/road" in tex_raw_path or "/material/terrain" in tex_raw_path or - "/material/custom" in tex_raw_path) + "/material/custom" in tex_raw_path or + "/umatlib" in tex_raw_path) # abort if empty texture or not aliased directory if not (tex_raw_path != "" and is_aliased_directory): @@ -872,11 +873,23 @@ def execute(self, context): mat_cont = mat_container.get_data_from_file(mat_abs_path) + # abort if data couldn't be recived + if not mat_cont: + self.report({'ERROR'}, "Couldn't read aliased material, aliasing aborted!") + return {'CANCELLED'} + + from io_scs_tools.internals.containers.parsers.mat_convert import AttributeConverter + converter = AttributeConverter() + # set attributes for attr_tuple in mat_cont.get_attributes().items(): - attr_key = attr_tuple[0] - attr_val = attr_tuple[1] + # convert attributes from effect to material format + if mat_cont.get_format() == "effect": + attr_key, attr_val = converter.effect_to_material(attr_tuple[0], attr_tuple[1]) + else: + attr_key = attr_tuple[0] + attr_val = attr_tuple[1] if attr_key == "substance": @@ -906,10 +919,40 @@ def execute(self, context): # set textures for tex_tuple in mat_cont.get_textures().items(): - if "shader_texture_" + tex_tuple[0] not in material.scs_props.keys(): - continue + # temp fix to not replace base texture cause it can have other name than .mat file + # "if" should be remove when aliasing detecting system will change from dds to mat + if not tex_tuple[0] == "texture_base": + + # convert local paths to raw paths + if not tex_tuple[1].startswith("/"): + file_name = _path_utils.get_filename(tex_raw_path) + raw_path = tex_raw_path.replace(file_name, "") + + # replace local path with raw path + tex_tuple = (tex_tuple[0], raw_path + tex_tuple[1]) + + if "shader_texture_" + tex_tuple[0][8:] not in material.scs_props.keys(): + continue + + setattr(material.scs_props, "shader_texture_" + tex_tuple[0][8:], tex_tuple[1]) + + # set tobjs + for tobj_tuple in mat_cont.get_tobjs().items(): + tex_attrs = tobj_tuple[1] + tobj_prop = getattr(material.scs_props, "shader_texture_" + tobj_tuple[0][8:] + "_settings", None) + + # ('u_repeat', 'v_repeat', 'tsnormal', 'color_space_linear') + tobj_prop.add("u_repeat") if "repeat" in tex_attrs[0] else tobj_prop.discard("u_repeat") + tobj_prop.add("v_repeat") if "repeat" in tex_attrs[1] else tobj_prop.discard("v_repeat") + # tobj_prop.add("w_repeat") if tex_attrs[12] and "repeat" in tex_attrs[2] else tobj_prop.discard("w_repeat") + + # check if tobj is nmap and set tsnormal + tobj_prop.add("tsnormal") if tobj_tuple[0][8:] == "nmap" else tobj_prop.discard("tsnormal") + + # check if tobj is in linear color space and set color_space_linear + # tobj_prop.add("color_space_linear") if (--TODO--) else tobj_prop.discard("color_space_linear") - setattr(material.scs_props, "shader_texture_" + tex_tuple[0], tex_tuple[1]) + setattr(material.scs_props, "shader_texture_" + tobj_tuple[0][8:] + "_settings", tobj_prop) # report success of aliasing if mat_cont.get_effect() == material.scs_props.mat_effect_name: diff --git a/addon/io_scs_tools/operators/mesh.py b/addon/io_scs_tools/operators/mesh.py index b6ec3ccd..531e0b14 100644 --- a/addon/io_scs_tools/operators/mesh.py +++ b/addon/io_scs_tools/operators/mesh.py @@ -26,6 +26,7 @@ from bpy.props import StringProperty, FloatProperty from io_scs_tools.consts import Mesh as _MESH_consts from io_scs_tools.consts import LampTools as _LT_consts +from io_scs_tools.consts import InteriorWindowTools as _IWT_consts from io_scs_tools.consts import VertexColorTools as _VCT_consts from io_scs_tools.utils import mesh as _mesh_utils from io_scs_tools.utils import view3d as _view3d_utils @@ -78,8 +79,10 @@ def execute(self, context): offset_x = 2 elif _LT_consts.VehicleSides.RearRight.name == self.vehicle_side: offset_x = 3 - elif _LT_consts.VehicleSides.Middle.name == self.vehicle_side: + elif _LT_consts.VehicleSides.MiddleLeft.name == self.vehicle_side: offset_x = 4 + elif _LT_consts.VehicleSides.MiddleRight.name == self.vehicle_side: + offset_x = 5 elif _LT_consts.AuxiliaryLampColors.White.name == self.aux_color: # auxiliary lights checking offset_x = 0 elif _LT_consts.AuxiliaryLampColors.Orange.name == self.aux_color: @@ -136,6 +139,117 @@ def execute(self, context): self.report({"INFO"}, "Lamp mask UV tool set %i faces to '%s'" % (polys_changed, changed_type)) return {'FINISHED'} +class InteriorWindowTool: + """ + Wrapper class for better navigation in file + """ + + class SCS_TOOLS_OT_SetGlassReflectionUV(bpy.types.Operator): + bl_label = "Set UV to glass" + bl_idname = "mesh.scs_tools_set_glassreflection_uv" + bl_description = "Sets offset for base interior UV according to given glass reflection state." + + glass_state: StringProperty( + description="", + default="", + options={'HIDDEN'}, + ) + + @classmethod + def poll(cls, context): + return context.object is not None and context.object.mode == "EDIT" + + def execute(self, context): + mesh = context.object.data + bm = bmesh.from_edit_mesh(mesh) # use bmesh module because we are working in edit mode + + # decide which offset to use depending on glass reflection state + offset_x = 0 + offset_y = 0 + if _IWT_consts.GlassReflection.Enable.name == self.glass_state: # glass reflection state checking + offset_y = 0 + elif _IWT_consts.GlassReflection.Disable.name == self.glass_state: + offset_y = 1 + else: + self.report({"ERROR"}, "Unsupported window glass state!") + return {"FINISHED"} + + polys_changed = 0 + for face in bm.faces: + + if face.select and len(context.object.material_slots) > 0: + material = context.object.material_slots[face.material_index].material + if material and len(material.scs_props.shader_texture_mask_uv) > 0: + + # use first mapping from mask texture + uv_lay_name = material.scs_props.shader_texture_mask_uv[0].value + + # if mask uv layer specified by current material doesn't exists + # move to next face + if uv_lay_name not in mesh.uv_layers: + self.report({"ERROR"}, "UV layer: '%s' not found in this object!" % uv_lay_name) + break + + uv_lay = bm.loops.layers.uv[uv_lay_name] + for loop in face.loops: + + uv = loop[uv_lay].uv + uv = (offset_x + (uv[0] - int(uv[0])), offset_y + (uv[1] - int(uv[1]))) + loop[uv_lay].uv = uv + + polys_changed += 1 + + # write data back if modified + if polys_changed > 0: + bmesh.update_edit_mesh(mesh) + + if self.glass_state != "": + changed_type = self.glass_state + else: + changed_type = "INVALID" + + self.report({"INFO"}, "Interior Window Tool set %i faces to '%s'" % (polys_changed, changed_type)) + return {'FINISHED'} + + + class SCS_TOOLS_OT_FixVertexNormals(bpy.types.Operator): + bl_label = "Fix Vertex Normals" + bl_idname = "mesh.scs_tools_fix_vertex_normals" + bl_description = "Fix Vertex Normals for proper interior paralax effect." + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + if context.object is None or context.object.mode != "OBJECT" or not context.object.select_get(): + return False + + material = context.object.active_material + if material and material.scs_props.mat_effect_name: + effect = material.scs_props.mat_effect_name + if "eut2.interior" in effect: + return True + + return False + + def execute(self, context): + mesh = context.object.data + + normals_changed = 0 + new_normals = [] + + # Change normals vector to direct upwards (0, 0, 1) + for loop in mesh.loops: + new_normals.append((0.0, 0.0, 1.0)) + normals_changed += 1 + + # Set the new split normals + mesh.normals_split_custom_set(new_normals) + mesh.update() + + + self.report({"INFO"}, "Changed %i split normals!" % normals_changed) + return {'FINISHED'} + class VertexColorTools: """ @@ -174,7 +288,8 @@ class SCS_TOOLS_OT_WrapVertexColors(bpy.types.Operator): def poll(cls, context): return context.object is not None and context.object.mode == "VERTEX_PAINT" and len(context.object.data.color_attributes) > 0 - def __init__(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.original_col = {} def __del__(self): @@ -280,8 +395,12 @@ def execute(self, context): layer_name = _MESH_consts.default_vcol layer_a_name = _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix + layer_factor_name = _MESH_consts.default_vfactor old_active_col_i = context.object.data.color_attributes.active_index + mat = context.active_object.active_material + vfactor_shader = ("piko.alldir") in mat.scs_props.mat_effect_name + for curr_lay_name in (layer_name, layer_a_name): if curr_lay_name not in context.object.data.color_attributes: @@ -293,6 +412,15 @@ def execute(self, context): for vertex_col_data in context.object.data.color_attributes[curr_lay_name].data: vertex_col_data.color = default_color + # factor vertex color layer + if vfactor_shader and layer_factor_name not in context.object.data.color_attributes: + vfcolor = context.object.data.color_attributes.new(name=layer_factor_name, type='BYTE_COLOR', domain='CORNER') + vfcolor.name = layer_factor_name # repeat naming step to make sure it's properly named + + # setting neutral value (0.5) to all factors + for vertex_fac_col_data in context.object.data.color_attributes[layer_factor_name].data: + vertex_fac_col_data.color = default_color + # restore active or set to default vcol if there was none if old_active_col_i is None: context.object.data.color_attributes.active_color = context.object.data.color_attributes[layer_name] @@ -316,6 +444,7 @@ def execute(self, context): layer_name = _MESH_consts.default_vcol layer_a_name = _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix + layer_factor_name = _MESH_consts.default_vfactor objs_using_active_material = [] @@ -335,6 +464,9 @@ def execute(self, context): for obj in objs_using_active_material: old_active_col_i = obj.data.color_attributes.active_index + mat = context.active_object.active_material + vfactor_shader = ("piko.alldir") in mat.scs_props.mat_effect_name + for curr_lay_name in (layer_name, layer_a_name): if curr_lay_name not in obj.data.color_attributes: @@ -345,6 +477,15 @@ def execute(self, context): # setting neutral value (0.5) to all colors for vertex_col_data in obj.data.color_attributes[curr_lay_name].data: vertex_col_data.color = default_color + + # factor vertex color layer + if vfactor_shader and layer_factor_name not in obj.data.color_attributes: + vfcolor = obj.data.color_attributes.new(name=layer_factor_name, type='BYTE_COLOR', domain='CORNER') + vfcolor.name = layer_factor_name # repeat naming step to make sure it's properly named + + # setting neutral value (0.0) to all factors + for vertex_fac_col_data in obj.data.color_attributes[layer_factor_name].data: + vertex_fac_col_data.color = default_color # restore active or set to default vcol if there was none if old_active_col_i is None: @@ -658,6 +799,9 @@ def execute(self, context): classes = ( LampTool.SCS_TOOLS_OT_SetLampmaskUV, + InteriorWindowTool.SCS_TOOLS_OT_SetGlassReflectionUV, + InteriorWindowTool.SCS_TOOLS_OT_FixVertexNormals, + VertexColorTools.SCS_TOOLS_OT_AddVertexColorsToActive, VertexColorTools.SCS_TOOLS_OT_AddVertexColorsToAll, VertexColorTools.SCS_TOOLS_OT_PrintVertexColorsStats, diff --git a/addon/io_scs_tools/operators/object.py b/addon/io_scs_tools/operators/object.py index c23bf74a..1c371d26 100755 --- a/addon/io_scs_tools/operators/object.py +++ b/addon/io_scs_tools/operators/object.py @@ -430,7 +430,7 @@ def execute(self, context): variant_part = _inventory.add_item(variant.parts, part.name) if variant_part: - variant_part.include = True + variant_part.include = True if _get_scs_globals().activate_new_parts else False else: lprint("W Part %r already in variant %r.", (part.name, variant.name)) @@ -604,6 +604,12 @@ class SCS_TOOLS_OT_AssignPart(bpy.types.Operator): bl_idname = "object.scs_tools_assign_part" bl_description = "Assign active SCS Part to selected objects" + part_index: IntProperty( + name="Part Index", + description="Index of the part to assign. If negative, uses active_scs_part.", + default=-1 + ) + @classmethod def poll(cls, context): if _object_utils.get_scs_root(context.active_object): @@ -616,7 +622,13 @@ def execute(self, context): active_object = context.active_object scs_root_object = _object_utils.get_scs_root(active_object) part_inventory = scs_root_object.scs_object_part_inventory - active_part_index = scs_root_object.scs_props.active_scs_part + + # Use part_index if provided, else use active_part_index + if self.part_index >= 0: + active_part_index = self.part_index + self.part_index = -1 # Reset to default after use + else: + active_part_index = scs_root_object.scs_props.active_scs_part scs_roots_count = len(_object_utils.gather_scs_roots(bpy.context.selected_objects)) if scs_roots_count == 1: @@ -778,7 +790,8 @@ def execute(self, context): for part in part_inventory: variant_part = _inventory.add_item(variant.parts, part.name) - variant_part.include = True + + variant_part.include = True if _get_scs_globals().activate_new_variant_parts else False scs_root_object.scs_props.active_scs_variant = len(variant_inventory) - 1 @@ -1247,7 +1260,7 @@ class SCS_TOOLS_OT_SwitchStaticCollisionObjects(bpy.types.Operator, _BaseViewOpe """Switch visibility of static collision objects.""" bl_label = "View Static Collision Objects" bl_idname = "object.scs_tools_switch_static_collision_objects" - bl_description = "View only objects which SCS Part is prefixed with 'coll', marking it as static collision object" + \ + bl_description = "View only objects which SCS Part is prefixed with 'coll', '_col' or suffixed with '_c', marking it as static collision object" + \ _BaseViewOperator.bl_base_description def execute(self, context): diff --git a/addon/io_scs_tools/operators/scene.py b/addon/io_scs_tools/operators/scene.py index fb03e18c..e13084f1 100644 --- a/addon/io_scs_tools/operators/scene.py +++ b/addon/io_scs_tools/operators/scene.py @@ -109,8 +109,9 @@ class SCS_TOOLS_OT_ExportByScope(bpy.types.Operator, _SCSExportHelper): bl_description = "Export SCS models depending on selected export scope." bl_options = set() - def __init__(self): - super().__init__() + def __init__(self, *args, **kwargs): + bpy.types.Operator.__init__(self, *args, **kwargs) + _SCSExportHelper.__init__(self, *args, **kwargs) self.can_mouse_rotate = False """:type bool: Flag indiciating whether mouse move even will rotate view or no. Initiated by left mouse button press.""" @@ -1071,7 +1072,8 @@ class SCS_TOOLS_OT_ConvertAllPaths(bpy.types.Operator): bl_idname = "scene.scs_tools_convert_all_paths" bl_description = "Converts all paths given in Custom Paths list + current SCS Project Base" - def __init__(self): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.include_current_project = True @classmethod diff --git a/addon/io_scs_tools/operators/wm.py b/addon/io_scs_tools/operators/wm.py index 9a90d984..cd31240f 100644 --- a/addon/io_scs_tools/operators/wm.py +++ b/addon/io_scs_tools/operators/wm.py @@ -19,6 +19,7 @@ # Copyright (C) 2013-2022: SCS Software +import gpu import bpy import os from bpy.props import StringProperty, BoolProperty, IntProperty @@ -136,13 +137,13 @@ def has_controls(window): @staticmethod def get_scs_banner_img_data(window): - """Loads image to blender data block, loads it to gl memory and gets bindcode address that can be used in - bgl module for image drawing. + """Loads image to blender data block, loads it to GPU memory and returns it's texture that can be used + for image drawing. :param window: window for which we should get banner image :type window: bpy.type.Window - :return: (bindcode of scs banner image, width of scs banner image, height of scs banner image - :rtype: (int, int, int) + :return: (texture, width of scs banner image, height of scs banner image + :rtype: (gpu.types.GPUTexture, int, int) """ if SCS_TOOLS_OT_Show3DViewReport.has_controls(window): @@ -151,22 +152,24 @@ def get_scs_banner_img_data(window): img_name = _OP_consts.View3DReport.BT_BANNER_IMG_NAME if img_name not in bpy.data.images: - img_path = os.path.join(_path_utils.get_addon_installation_paths()[0], "ui", "banners", img_name) img = bpy.data.images.load(img_path, check_existing=True) img.colorspace_settings.name = 'sRGB' img.alpha_mode = 'CHANNEL_PACKED' else: - img = bpy.data.images[img_name] - # ensure that image is loaded in GPU memory aka has proper bindcode, - # we have to that each time because if operator is shown for long time blender might free it on it's own - if img.bindcode == 0: - img.gl_load() + if not img.has_data: + img.reload() + + try: + texture = gpu.texture.from_image(img) + except RuntimeError: + img.reload() + texture = gpu.texture.from_image(img) - return img.bindcode, img.size[0], img.size[1] + return texture, img.size[0], img.size[1] @staticmethod def get_lines(): diff --git a/addon/io_scs_tools/properties/addon_preferences.py b/addon/io_scs_tools/properties/addon_preferences.py index 2ee88dce..ef8ed8a8 100644 --- a/addon/io_scs_tools/properties/addon_preferences.py +++ b/addon/io_scs_tools/properties/addon_preferences.py @@ -140,14 +140,26 @@ def retrieve_shader_presets_items(self, context): icon_str = 'MATERIAL' elif "glass" in preset_name: icon_str = 'MOD_LATTICE' + elif "grass" in preset_name: + icon_str = 'STRANDS' + elif "interior" in preset_name: + icon_str = 'OUTLINER_OB_LATTICE' elif "lamp" in preset_name: icon_str = 'LIGHT_SPOT' + elif "mlaa" in preset_name: + icon_str = 'GROUP_UVS' + elif "none" in preset_name and preset_name != "": + icon_str = 'MESH_CIRCLE' + elif "particle" in preset_name: + icon_str = 'PARTICLE_TIP' elif "shadowonly" in preset_name: icon_str = 'MAT_SPHERE_SKY' elif "truckpaint" in preset_name: icon_str = 'AUTO' - elif "mlaa" in preset_name: - icon_str = 'GROUP_UVS' + elif "water" in preset_name: + icon_str = 'MOD_OCEAN' + elif "window" in preset_name: + icon_str = 'OUTLINER_OB_LATTICE' elif preset_name == "": icon_str = 'X' else: @@ -547,6 +559,42 @@ def locator_coll_face_color_update(self, context): self.on_display_setting_update(context) _config_container.update_item_in_file('GlobalColors.ColliderLocatorsFace', tuple(self.locator_coll_face_color)) + def show_trailer_type_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalDisplay.ShowTrailerType', int(self.show_trailer_type)) + + def trailer_load_easy_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.TrailerLoadEasy', tuple(self.trailer_load_easy_color)) + + def trailer_load_medium_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.TrailerLoadMedium', tuple(self.trailer_load_medium_color)) + + def trailer_load_hard_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.TrailerLoadHard', tuple(self.trailer_load_hard_color)) + + def trailer_unload_easy_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.TrailerUnloadEasy', tuple(self.trailer_unload_easy_color)) + + def trailer_unload_medium_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.TrailerUnloadMedium', tuple(self.trailer_unload_medium_color)) + + def trailer_unload_hard_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.TrailerUnloadHard', tuple(self.trailer_unload_hard_color)) + + def owned_trailer_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.OwnedTrailer', tuple(self.owned_trailer_color)) + + def service_station_color_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalColors.ServiceStation', tuple(self.service_station_color)) + def display_connections_update(self, context): self.on_display_setting_update(context) _config_container.update_item_in_file('GlobalDisplay.DisplayConnections', int(self.display_connections)) @@ -721,6 +769,92 @@ def get_icon_theme_item(self): # default=(0.065, 0.18, 0.3), update=locator_coll_face_color_update, ) + show_trailer_type: BoolProperty( + name="Show Trailer Type", + description="Show trailer type shape for custom spawn point locators", + default=True, + update=show_trailer_type_update + ) + trailer_load_easy_color: FloatVectorProperty( + name="(Easy) Trailer Load", + description="Color of trailer loading zone in 3D views - easy difficulty", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(0.0, 1.0, 1.0, 1.0), + update=trailer_load_easy_color_update, + ) + trailer_load_medium_color: FloatVectorProperty( + name="(Medium) Trailer Load", + description="Color of trailer loading zone in 3D views - medium difficulty", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(0.0, 0.471, 1.0, 1.0), + update=trailer_load_medium_color_update, + ) + trailer_load_hard_color: FloatVectorProperty( + name="(Hard) Trailer Load", + description="Color of trailer loading zone in 3D views - hard difficulty", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(0.0, 0.0, 0.784, 1.0), + update=trailer_load_hard_color_update, + ) + trailer_unload_easy_color: FloatVectorProperty( + name="(Easy) Trailer Unload", + description="Color of trailer unloading zone in 3D views - easy difficulty", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(1.0, 1.0, 0.0, 1.0), + update=trailer_unload_easy_color_update, + ) + trailer_unload_medium_color: FloatVectorProperty( + name="(Medium) Trailer Unload", + description="Color of trailer unloading zone in 3D views - medium difficulty", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(0.706, 0.471, 0.0, 1.0), + update=trailer_unload_medium_color_update, + ) + trailer_unload_hard_color: FloatVectorProperty( + name="(Hard) Trailer Unload", + description="Color of trailer unloading zone in 3D views - hard difficulty", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(1.0, 0.0, 0.0, 1.0), + update=trailer_unload_hard_color_update, + ) + owned_trailer_color: FloatVectorProperty( + name="Owned Trailer Color", + description="Color for Owned Trailer zone in 3D views", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(1.0, 0.0, 1.0, 1.0), + update=owned_trailer_color_update, + ) + service_station_color: FloatVectorProperty( + name="Service Station Color", + description="Color for Service Station zone in 3D views", + options={'HIDDEN'}, + subtype='COLOR', + size = 4, + min=0, max=1, + default=(0.0, 0.706, 0.0, 1.0), + update=service_station_color_update, + ) display_connections: BoolProperty( name="Display Connections", description="Display connections in 3D views", @@ -1206,6 +1340,14 @@ def config_storage_place_update(self, context): return None + def activate_new_parts_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalOther.ActivateNewParts', int(self.activate_new_parts)) + + def activate_new_variant_parts_update(self, context): + self.on_display_setting_update(context) + _config_container.update_item_in_file('GlobalOther.ActivateNewVariantParts', int(self.activate_new_variant_parts)) + dump_level: EnumProperty( name="Printouts", items=( @@ -1230,6 +1372,20 @@ def config_storage_place_update(self, context): update=config_storage_place_update, ) + activate_new_parts: BoolProperty( + name="Activate New Parts", + description="Automatically activate new parts when added to SCS Game Object.", + default=True, + update=activate_new_parts_update + ) + + activate_new_variant_parts: BoolProperty( + name="Activate New Variant Parts", + description="Automatically activate parts when new variant is created for SCS Game Object.", + default=True, + update=activate_new_variant_parts_update + ) + # COMMON SETTINGS - NOT SAVED IN CONFIG preview_export_selection: BoolProperty( name="Preview selection", diff --git a/addon/io_scs_tools/properties/dynamic/__init__.py b/addon/io_scs_tools/properties/dynamic/__init__.py index ece47add..cdb8fb25 100644 --- a/addon/io_scs_tools/properties/dynamic/__init__.py +++ b/addon/io_scs_tools/properties/dynamic/__init__.py @@ -82,12 +82,12 @@ def getter(self): prefs = bpy.context.preferences.addons["io_scs_tools"].preferences - if scope not in prefs: + if not hasattr(prefs, scope): return default scoped_prefs = prefs[scope] - if property_name not in scoped_prefs: + if not hasattr(scoped_prefs, property_name): return default return scoped_prefs[property_name] @@ -105,10 +105,12 @@ def setter(self, value): prefs = bpy.context.preferences.addons["io_scs_tools"].preferences - if scope not in prefs: - prefs[scope] = {} - - prefs[scope][property_name] = value + if not hasattr(prefs, scope): + setattr(prefs, scope, {}) + + scope_dict = getattr(prefs, scope) + scope_dict[property_name] = value + setattr(prefs, scope, scope_dict) # check for default value type assert isinstance(default, property_type) diff --git a/addon/io_scs_tools/properties/material.py b/addon/io_scs_tools/properties/material.py index b37db796..3bc5ac31 100644 --- a/addon/io_scs_tools/properties/material.py +++ b/addon/io_scs_tools/properties/material.py @@ -129,7 +129,9 @@ def __update_shader_texture_tobj_file__(self, context, tex_type): if texture_raw_path.endswith(".tobj"): texture_name = _path_utils.get_texture_path_from_tobj(tobj_file, raw_value=True) else: - texture_name = os.path.basename(texture_raw_path) + # path must be expanded to absolute path becasue basename doesn't support paths with "//" + abs_texture_path = bpy.path.abspath(texture_raw_path) + texture_name = os.path.basename(abs_texture_path) # update last tobj load time if export was successful otherwise report saving problems if _tobj_exp.export(tobj_file, texture_name, texture_settings): @@ -238,6 +240,42 @@ def update_value(self, context): tex_coord: IntProperty(default=-1) # used upon export for mapping uv to tex_coord field texture_type: StringProperty() # used in update function to be able to identify which texture should be updated + class UVAddMappingItem(bpy.types.PropertyGroup): + """Class for saving uv map reference for SCS mapping + """ + + def update_value(self, context): + __update_look__(self, context) + + material = _material_utils.get_material_from_context(context) + + if material: + _shader.set_mapping(material, self.mapping_type, self.value, self.tex_coord) + + # synchronize all scs mappings that uses the same tex_coord field in current material + if "scs_shader_attributes" in material and "mappings" in material["scs_shader_attributes"]: + for mapping_entry in material["scs_shader_attributes"]["mappings"].values(): + if "Tag" in mapping_entry: + curr_map_type = mapping_entry["Tag"] + if curr_map_type != self.mapping_type: # if different mapping from current + uv_mappings = getattr(material.scs_props, "shader_mapping_" + curr_map_type, []) + if uv_mappings and len(uv_mappings) > 0: + for uv_mapping in uv_mappings: + # if tex_coord props are the same and uv mapping differs then set current uv mapping value to it + if self.tex_coord != -1 and uv_mapping.tex_coord == self.tex_coord and uv_mapping.value != self.value: + uv_mapping.value = self.value + + value: StringProperty( + name="Mapping UV Set", + description="Mapping of UV Set to current mapping type", + default="", + options={'HIDDEN'}, + update=update_value + ) + + tex_coord: IntProperty(default=-1) # used upon export for mapping uv to tex_coord field + mapping_type: StringProperty() # used in update function to be able to identify which mapping should be updated + class TexCoordItem(bpy.types.PropertyGroup): """Property group holding data how to map uv maps to exported PIM file. It should be used only on material with imported shader. """ @@ -271,9 +309,11 @@ def get_texture_types(): :return: SCS Shader Texture Types :rtype: dict """ - return {'base', 'reflection', 'over', 'oclu', 'mask', 'mult', 'iamod', 'lightmap', 'paintjob', - 'flakenoise', 'nmap', 'base_1', 'mult_1', 'detail', 'nmap_detail', 'layer0', 'layer1', - 'sky_weather_base_a', 'sky_weather_base_b', 'sky_weather_over_a', 'sky_weather_over_b'} + return {'base', 'reflection', 'over', 'oclu', 'mask' , 'mask_1' , 'mask_2', 'mask_3', 'mult', 'iamod', 'lightmap', 'paintjob', + 'flakenoise', 'nmap', 'base_1', 'mult_1', 'detail', 'nmap_detail', 'nmap_over' , 'layer0', 'layer1', + 'sky_weather_base_a', 'sky_weather_base_b', 'sky_weather_over_a', 'sky_weather_over_b', + 'sky_weather_base_mask_a', 'sky_weather_base_mask_b', 'sky_weather_over_mask_a', 'sky_weather_over_mask_b', + 'heightmap', 'heightmap_over', 'heightmap_jitter'} def get_id(self): """Gets unique ID for material within current Blend file. If ID does not exists yet it's calculated. @@ -396,6 +436,24 @@ def update_shader_texture_mask(self, context): def update_shader_texture_mask_settings(self, context): __update_shader_texture_tobj_file__(self, context, "mask") + def update_shader_texture_mask_1(self, context): + __update_shader_texture__(self, context, "mask_1") + + def update_shader_texture_mask_1_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "mask_1") + + def update_shader_texture_mask_2(self, context): + __update_shader_texture__(self, context, "mask_2") + + def update_shader_texture_mask_2_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "mask_2") + + def update_shader_texture_mask_3(self, context): + __update_shader_texture__(self, context, "mask_3") + + def update_shader_texture_mask_3_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "mask_3") + def update_shader_texture_mult(self, context): __update_shader_texture__(self, context, "mult") @@ -420,6 +478,12 @@ def update_shader_texture_nmap_detail(self, context): def update_shader_texture_nmap_detail_settings(self, context): __update_shader_texture_tobj_file__(self, context, "nmap_detail") + def update_shader_texture_nmap_over(self, context): + __update_shader_texture__(self, context, "nmap_over") + + def update_shader_texture_nmap_over_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "nmap_over") + def update_shader_texture_oclu(self, context): __update_shader_texture__(self, context, "oclu") @@ -468,6 +532,49 @@ def update_shader_texture_sky_weather_over_b(self, context): def update_shader_texture_sky_weather_over_b_settings(self, context): __update_shader_texture_tobj_file__(self, context, "sky_weather_over_b") + def update_shader_texture_sky_weather_base_mask_a(self, context): + __update_shader_texture__(self, context, "sky_weather_base_mask_a") + + def update_shader_texture_sky_weather_base_mask_a_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "sky_weather_base_mask_a") + + def update_shader_texture_sky_weather_base_mask_b(self, context): + __update_shader_texture__(self, context, "sky_weather_base_mask_b") + + def update_shader_texture_sky_weather_base_mask_b_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "sky_weather_base_mask_b") + + def update_shader_texture_sky_weather_over_mask_a(self, context): + __update_shader_texture__(self, context, "sky_weather_over_mask_a") + + def update_shader_texture_sky_weather_over_mask_a_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "sky_weather_over_mask_a") + + def update_shader_texture_sky_weather_over_mask_b(self, context): + __update_shader_texture__(self, context, "sky_weather_over_mask_b") + + def update_shader_texture_sky_weather_over_mask_b_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "sky_weather_over_mask_b") + + def update_shader_texture_heightmap(self, context): + __update_shader_texture__(self, context, "heightmap") + + def update_shader_texture_heightmap_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "heightmap") + + def update_shader_texture_heightmap_over(self, context): + __update_shader_texture__(self, context, "heightmap_over") + + def update_shader_texture_heightmap_over_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "heightmap_over") + + def update_shader_texture_heightmap_jitter(self, context): + __update_shader_texture__(self, context, "heightmap_jitter") + + def update_shader_texture_heightmap_jitter_settings(self, context): + __update_shader_texture_tobj_file__(self, context, "heightmap_jitter") + + _cached_mat_num = -1 """Caching number of all materials to properly fix material ids when duplicating material""" @@ -1176,6 +1283,159 @@ def update_shader_texture_sky_weather_over_b_settings(self, context): options={'HIDDEN'}, ) + # TEXTURE: MASK_1 + shader_texture_mask_1: StringProperty( + name="Texture Mask", + description="Texture Mask for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_mask_1, + ) + shader_texture_mask_1_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_mask_1_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_mask_1_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_mask_1_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_mask_1_settings + ) + shader_texture_mask_1_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_mask_1_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_mask_1_uv: CollectionProperty( + name="Texture Mask UV Sets", + description="Texture Mask UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: MASK_2 + shader_texture_mask_2: StringProperty( + name="Texture Mask", + description="Texture Mask for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_mask_2, + ) + shader_texture_mask_2_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_mask_2_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_mask_2_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_mask_2_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_mask_2_settings + ) + shader_texture_mask_2_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_mask_2_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_mask_2_uv: CollectionProperty( + name="Texture Mask UV Sets", + description="Texture Mask UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: MASK_3 + shader_texture_mask_3: StringProperty( + name="Texture Mask", + description="Texture Mask for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_mask_3, + ) + shader_texture_mask_3_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_mask_3_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_mask_3_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_mask_3_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_mask_3_settings + ) + shader_texture_mask_3_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_mask_3_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_mask_3_uv: CollectionProperty( + name="Texture Mask UV Sets", + description="Texture Mask UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + # TEXTURE: MULT shader_texture_mult: StringProperty( name="Texture Mult", @@ -1380,6 +1640,57 @@ def update_shader_texture_sky_weather_over_b_settings(self, context): options={'HIDDEN'}, ) + # TEXTURE: NMAP_OVER + shader_texture_nmap_over: StringProperty( + name="Texture NMap Over", + description="Texture Nmap Over for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_nmap_over, + ) + shader_texture_nmap_over_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_nmap_over_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_nmap_over_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_nmap_over_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_nmap_over_settings + ) + shader_texture_nmap_over_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_nmap_over_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_nmap_over_uv: CollectionProperty( + name="Texture Nmap Over UV Sets", + description="Texture Nmap Over UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + # TEXTURE: OCCLUSION shader_texture_oclu: StringProperty( name="Texture Oclu", @@ -1788,6 +2099,383 @@ def update_shader_texture_sky_weather_over_b_settings(self, context): options={'HIDDEN'}, ) + # TEXTURE: SKY_WEATHER_BASE_MASK_A + shader_texture_sky_weather_base_mask_a: StringProperty( + name="Texture Sky Weather Base Mask A", + description="Texture Sky Weather Base Mask A for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_sky_weather_base_mask_a, + ) + shader_texture_sky_weather_base_mask_a_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_sky_weather_base_mask_a_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_sky_weather_base_mask_a_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_sky_weather_base_mask_a_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_sky_weather_base_mask_a_settings + ) + shader_texture_sky_weather_base_mask_a_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_sky_weather_base_mask_a_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_sky_weather_base_mask_a_uv: CollectionProperty( + name="Texture Sky Weather Base Mask A UV Sets", + description="Texture Sky Weather Base Mask A UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: SKY_WEATHER_BASE_MASK_B + shader_texture_sky_weather_base_mask_b: StringProperty( + name="Texture Sky Weather Base Mask B", + description="Texture Sky Weather Base Mask B for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_sky_weather_base_mask_b, + ) + shader_texture_sky_weather_base_mask_b_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_sky_weather_base_mask_b_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_sky_weather_base_mask_b_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_sky_weather_base_mask_b_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_sky_weather_base_mask_b_settings + ) + shader_texture_sky_weather_base_mask_b_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_sky_weather_base_mask_b_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_sky_weather_base_mask_b_uv: CollectionProperty( + name="Texture Sky Weather Base Mask B UV Sets", + description="Texture Sky Weather Base Mask B UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: SKY_WEATHER_OVER_MASK_A + shader_texture_sky_weather_over_mask_a: StringProperty( + name="Texture Sky Weather Over Mask A", + description="Texture Sky Weather Over Mask A for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_sky_weather_over_mask_a, + ) + shader_texture_sky_weather_over_mask_a_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_sky_weather_over_mask_a_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_sky_weather_over_mask_a_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_sky_weather_over_mask_a_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_sky_weather_over_mask_a_settings + ) + shader_texture_sky_weather_over_mask_a_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_sky_weather_over_mask_a_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_sky_weather_over_mask_a_uv: CollectionProperty( + name="Texture Sky Weather Over Mask A UV Sets", + description="Texture Sky Weather Over Mask A UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: SKY_WEATHER_OVER_MASK_B + shader_texture_sky_weather_over_mask_b: StringProperty( + name="Texture Sky Weather Over Mask B", + description="Texture Sky Weather Over Mask B for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_sky_weather_over_mask_b, + ) + shader_texture_sky_weather_over_mask_b_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_sky_weather_over_mask_b_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_sky_weather_over_mask_b_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_sky_weather_over_mask_b_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_sky_weather_over_mask_b_settings + ) + shader_texture_sky_weather_over_mask_b_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_sky_weather_over_mask_b_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_sky_weather_over_mask_b_uv: CollectionProperty( + name="Texture Sky Weather Over Mask B UV Sets", + description="Texture Sky Weather Over Mask B UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: HEIGHTMAP + shader_texture_heightmap: StringProperty( + name="Texture Heightmap", + description="Texture Heightmap for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_heightmap, + ) + shader_texture_heightmap_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_heightmap_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_heightmap_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_heightmap_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_heightmap_settings + ) + shader_texture_heightmap_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_heightmap_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_heightmap_uv: CollectionProperty( + name="Texture Heightmap UV Sets", + description="Texture Heightmap UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: HEIGHTMAP_OVER + shader_texture_heightmap_over: StringProperty( + name="Texture Heightmap Over", + description="Texture Heightmap Over for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_heightmap_over, + ) + shader_texture_heightmap_over_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_heightmap_over_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_heightmap_over_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_heightmap_over_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_heightmap_over_settings + ) + shader_texture_heightmap_over_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_heightmap_over_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_heightmap_over_uv: CollectionProperty( + name="Texture Heightmap Over UV Sets", + description="Texture Heightmap Over UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # TEXTURE: HEIGHTMAP_JITTER + shader_texture_heightmap_jitter: StringProperty( + name="Texture Heightmap Jitter", + description="Texture Heightmap Jitter for active Material", + default=_MAT_consts.unset_bitmap_filepath, + options={'HIDDEN'}, + subtype='NONE', + update=update_shader_texture_heightmap_jitter, + ) + shader_texture_heightmap_jitter_imported_tobj: StringProperty( + name="Imported TOBJ Path", + description="Use imported TOBJ path reference which will be exported into material (NOTE: export will not take care of any TOBJ files!)", + default="", + update=__update_look__ + ) + shader_texture_heightmap_jitter_locked: BoolProperty( + name="Texture Locked", + description="Tells if texture is locked and should not be changed by user(intended for internal usage only)", + default=False + ) + shader_texture_heightmap_jitter_map_type: StringProperty( + name="Texture Map Type", + description="Stores texture mapping type and should not be changed by user(intended for internal usage only)", + default="2d" + ) + shader_texture_heightmap_jitter_settings: EnumProperty( + name="Settings", + description="TOBJ settings for this texture", + items=__get_texture_settings__(), + default=set(), + options={'ENUM_FLAG'}, + update=update_shader_texture_heightmap_jitter_settings + ) + shader_texture_heightmap_jitter_tobj_load_time: StringProperty( + name="Last TOBJ load time", + description="Time string of last loading", + default="", + ) + shader_texture_heightmap_jitter_use_imported: BoolProperty( + name="Use Imported", + description="Use custom provided path for TOBJ reference", + default=False, + update=__update_look__ + ) + shader_texture_heightmap_jitter_uv: CollectionProperty( + name="Texture Heightmap Jitter UV Sets", + description="Texture Heightmap Jitter UV sets for active Material", + type=UVMappingItem, + options={'HIDDEN'}, + ) + + # MAPPING: PERTURBATION + shader_mapping_unknown: CollectionProperty( + name="Unknown UV Sets", + description="Unknown UV sets for active Material", + type=UVAddMappingItem, + options={'HIDDEN'}, + ) + shader_mapping_perturbation: CollectionProperty( + name="Perturbation UV Sets", + description="Perturbation UV sets for active Material", + type=UVAddMappingItem, + options={'HIDDEN'}, + ) + shader_mapping_vertex_pos: CollectionProperty( + name="Vertex position UV Sets", + description="Vertex position UV sets for active Material", + type=UVAddMappingItem, + options={'HIDDEN'}, + ) + # # Following String property is fed from MATERIAL SUBSTANCE data list, which is usually loaded from # # "//base/material/material.db" file and stored at "scs_inventories.matsubs". substance: StringProperty( @@ -1803,6 +2491,7 @@ def update_shader_texture_sky_weather_over_b_settings(self, context): MaterialSCSTools.TexCoordItem, MaterialSCSTools.AuxiliaryItem, MaterialSCSTools.UVMappingItem, + MaterialSCSTools.UVAddMappingItem, MaterialSCSTools ) diff --git a/addon/io_scs_tools/properties/object.py b/addon/io_scs_tools/properties/object.py index 606c9d38..a4aa2c51 100644 --- a/addon/io_scs_tools/properties/object.py +++ b/addon/io_scs_tools/properties/object.py @@ -90,16 +90,17 @@ def name_update(self, context): scs_root_obj = _object_utils.get_scs_root(context.active_object) # if there is more of parts with same name, make postfixed name (this will cause another name update) - if len(_inventory.get_indices(scs_root_obj.scs_object_part_inventory, self.name)) == 2: # duplicate + if scs_root_obj != None: + if len(_inventory.get_indices(scs_root_obj.scs_object_part_inventory, self.name)) == 2: # duplicate - i = 1 - new_name = _name_utils.tokenize_name(self.name + "_" + str(i).zfill(2)) - while _inventory.get_index(scs_root_obj.scs_object_part_inventory, new_name) != -1: + i = 1 new_name = _name_utils.tokenize_name(self.name + "_" + str(i).zfill(2)) - i += 1 + while _inventory.get_index(scs_root_obj.scs_object_part_inventory, new_name) != -1: + new_name = _name_utils.tokenize_name(self.name + "_" + str(i).zfill(2)) + i += 1 - if new_name != self.name: - self.name = new_name + if new_name != self.name: + self.name = new_name if "scs_part_old_name" in self: @@ -294,9 +295,15 @@ def update_empty_object_type(self, context): obj.empty_display_type = "ARROWS" obj.show_name = True - # ensure default part + # ensure default part when it's needed part_inventory = obj.scs_object_part_inventory - _inventory.add_item(part_inventory, _PART_consts.default_name, conditional=True) + skip_default_part = obj.get("skip_default_part", False) + if not skip_default_part: + _inventory.add_item(part_inventory, _PART_consts.default_name, conditional=True) + + # remove flag after use + if "skip_default_part" in obj: + del obj["skip_default_part"] else: obj.empty_display_size = 1.0 obj.empty_display_type = "PLAIN_AXES" @@ -368,6 +375,9 @@ def active_scs_part_get_direct(self): # NOTE: case where this happens is if user imports SCS model, # duplicates prefab locator without part and then changes locator # type to model locator. + if "active_scs_part_old_active" not in self: + self["active_scs_part_old_active"] = "" + if "active_scs_part_value" not in self: self["active_scs_part_value"] = 0 @@ -379,26 +389,13 @@ def active_scs_part_get(self): """ - if "active_scs_part_old_active" not in self: - self["active_scs_part_old_active"] = "" - - if "active_scs_part_value" not in self: - self["active_scs_part_value"] = 0 - - scs_root_object = _object_utils.get_scs_root(bpy.context.active_object) - if scs_root_object and bpy.context.active_object != scs_root_object: - - # if old active object is different than current - # set the value for active part index from it - if self["active_scs_part_old_active"] != bpy.context.active_object.name: - self["active_scs_part_value"] = _inventory.get_index(scs_root_object.scs_object_part_inventory, - bpy.context.active_object.scs_props.scs_part) - - self["active_scs_part_old_active"] = bpy.context.active_object.name - return self["active_scs_part_value"] + return self.active_scs_part_value def active_scs_part_set(self, value): - self["active_scs_part_value"] = value + """Store the index selected by the user. + """ + + self.active_scs_part_value = value active_scs_part: IntProperty( name="Active SCS Part", @@ -412,6 +409,22 @@ def active_scs_part_set(self, value): set=active_scs_part_set ) + active_scs_part_old_active: bpy.props.StringProperty( + name="Old Active Part", + description="Stores previously active SCS part", + default="" + ) + + active_scs_part_value: IntProperty( + name="Active SCS Part Value", + description="Active SCS Part for current SCS Root Object Value", + default=0, + min=0, + step=1, + options={'HIDDEN'}, + subtype='NONE' + ) + def get_active_scs_look(self): if "active_scs_look" not in self: @@ -590,7 +603,7 @@ def locator_preview_model_type_update(self, context): for child in obj.children: - if "scs_props" in child.data and child.data.scs_props.locator_preview_model_path != "": + if hasattr(child.data, "scs_props") and child.data.scs_props.locator_preview_model_path != "": child.display_type = obj.scs_props.locator_preview_model_type @@ -850,7 +863,7 @@ def locator_prefab_type_update(self, context): (_PL_consts.PSP.CAMERA_POINT, (str(_PL_consts.PSP.CAMERA_POINT), "Camera Point", "")), (_PL_consts.PSP.COMPANY_POS, (str(_PL_consts.PSP.COMPANY_POS), "Company Point", "")), (_PL_consts.PSP.COMPANY_UNLOAD_POS, (str(_PL_consts.PSP.COMPANY_UNLOAD_POS), "Company Unload Point", "")), - # (_PL_consts.PSP.CUSTOM, (str(_PL_consts.PSP.CUSTOM), "Custom", "")), + (_PL_consts.PSP.CUSTOM, (str(_PL_consts.PSP.CUSTOM), "Custom", "")), (_PL_consts.PSP.GARAGE_POS, (str(_PL_consts.PSP.GARAGE_POS), "Garage Point", "")), (_PL_consts.PSP.GAS_POS, (str(_PL_consts.PSP.GAS_POS), "Gas Station", "")), # (_PL_consts.PSP.HOTEL, (str(_PL_consts.PSP.HOTEL), "Hotel", "")), @@ -867,7 +880,7 @@ def locator_prefab_type_update(self, context): (_PL_consts.PSP.UNLOAD_EASY_POS, (str(_PL_consts.PSP.UNLOAD_EASY_POS), "Unload (Easy)", "")), (_PL_consts.PSP.UNLOAD_MEDIUM_POS, (str(_PL_consts.PSP.UNLOAD_MEDIUM_POS), "Unload (Medium)", "")), (_PL_consts.PSP.UNLOAD_HARD_POS, (str(_PL_consts.PSP.UNLOAD_HARD_POS), "Unload (Hard)", "")), - (_PL_consts.PSP.UNLOAD_RIGID_POS, (str(_PL_consts.PSP.UNLOAD_RIGID_POS), "Unload (Rigid)", "")), + (_PL_consts.PSP.UNLOAD_RIGID_POS, (str(_PL_consts.PSP.UNLOAD_RIGID_POS), "Unload (Rigid) (Deprecated)", "")), (_PL_consts.PSP.WEIGHT_POS, (str(_PL_consts.PSP.WEIGHT_POS), "Weight Station", "")), (_PL_consts.PSP.WEIGHT_CAT_POS, (str(_PL_consts.PSP.WEIGHT_CAT_POS), "Weight Station CAT", "")), ]) @@ -878,6 +891,70 @@ def locator_prefab_type_update(self, context): items=enum_spawn_type_items.values(), default=str(_PL_consts.PSP.NONE), ) + # LOCATORS - PREFAB - SPAWN POINTS (CUSTOM) + enum_custom_depot_type_items = OrderedDict([ + (_PL_consts.PSPCF.DEPOT_TYPE_UNLOAD, (str(_PL_consts.PSPCF.DEPOT_TYPE_UNLOAD), "Unload", "")), + (_PL_consts.PSPCF.DEPOT_TYPE_LOAD, (str(_PL_consts.PSPCF.DEPOT_TYPE_LOAD), "Load", "")), + ]) + locator_prefab_custom_depot_type: EnumProperty( + name="Depot Type", + description="Depot Type", + items=enum_custom_depot_type_items.values(), + default=str(_PL_consts.PSPCF.DEPOT_TYPE_UNLOAD), + ) + enum_custom_parking_difficulty_items = OrderedDict([ + (_PL_consts.PSPCF.DIFFICULTY_NONE, (str(_PL_consts.PSPCF.DIFFICULTY_NONE), "None", "")), + (_PL_consts.PSPCF.DIFFICULTY_EASY, (str(_PL_consts.PSPCF.DIFFICULTY_EASY), "Easy", "")), + (_PL_consts.PSPCF.DIFFICULTY_MEDIUM, (str(_PL_consts.PSPCF.DIFFICULTY_MEDIUM), "Medium", "")), + (_PL_consts.PSPCF.DIFFICULTY_HARD, (str(_PL_consts.PSPCF.DIFFICULTY_HARD), "Hard", "")), + ]) + locator_prefab_custom_parking_difficulty: EnumProperty( + name="Parking Difficulty", + description="Current parking difficulty", + items=enum_custom_parking_difficulty_items.values(), + default=str(_PL_consts.PSPCF.DIFFICULTY_EASY), + ) + enum_custom_lenght_items = OrderedDict([ + (_PL_consts.PSPCF.LENGHT_14, (str(_PL_consts.PSPCF.LENGHT_14), "14 m", "")), + (_PL_consts.PSPCF.LENGHT_15, (str(_PL_consts.PSPCF.LENGHT_15), "15 m", "")), + (_PL_consts.PSPCF.LENGHT_16, (str(_PL_consts.PSPCF.LENGHT_16), "16 m", "")), + (_PL_consts.PSPCF.LENGHT_17, (str(_PL_consts.PSPCF.LENGHT_17), "17 m", "")), + (_PL_consts.PSPCF.LENGHT_18, (str(_PL_consts.PSPCF.LENGHT_18), "18 m", "")), + (_PL_consts.PSPCF.LENGHT_19, (str(_PL_consts.PSPCF.LENGHT_19), "19 m", "")), + (_PL_consts.PSPCF.LENGHT_20, (str(_PL_consts.PSPCF.LENGHT_20), "20 m", "")), + (_PL_consts.PSPCF.LENGHT_21, (str(_PL_consts.PSPCF.LENGHT_21), "21 m", "")), + (_PL_consts.PSPCF.LENGHT_22, (str(_PL_consts.PSPCF.LENGHT_22), "22 m", "")), + (_PL_consts.PSPCF.LENGHT_23, (str(_PL_consts.PSPCF.LENGHT_23), "23 m", "")), + (_PL_consts.PSPCF.LENGHT_24, (str(_PL_consts.PSPCF.LENGHT_24), "24 m", "")), + (_PL_consts.PSPCF.LENGHT_25, (str(_PL_consts.PSPCF.LENGHT_25), "25 m", "")), + (_PL_consts.PSPCF.LENGHT_26, (str(_PL_consts.PSPCF.LENGHT_26), "26 m", "")), + (_PL_consts.PSPCF.LENGHT_27, (str(_PL_consts.PSPCF.LENGHT_27), "27 m", "")), + (_PL_consts.PSPCF.LENGHT_28, (str(_PL_consts.PSPCF.LENGHT_28), "28 m", "")), + (_PL_consts.PSPCF.UNLIMITED, (str(_PL_consts.PSPCF.UNLIMITED), "Unlimited", "")), + ]) + locator_prefab_custom_lenght: EnumProperty( + name="Trailer Lenght", + description="Max trailer lenght", + options={'HIDDEN'}, + items=enum_custom_lenght_items.values(), + default=str(_PL_consts.PSPCF.LENGHT_14), + ) + enum_custom_rule_items = OrderedDict([ + (_PL_consts.PSPCF.TRAILER_ANY, (str(_PL_consts.PSPCF.TRAILER_ANY), "Any Trailer", "")), + (_PL_consts.PSPCF.TRAILER_BOX, (str(_PL_consts.PSPCF.TRAILER_BOX), "Box Trailer", "")), + (_PL_consts.PSPCF.TRAILER_TANK, (str(_PL_consts.PSPCF.TRAILER_TANK), "Tank Trailer", "")), + (_PL_consts.PSPCF.TRAILER_DUMP_BULK, (str(_PL_consts.PSPCF.TRAILER_DUMP_BULK), "Dump & Bulk", "")), + (_PL_consts.PSPCF.TRAILER_PLATFORM_LOG_CONT, (str(_PL_consts.PSPCF.TRAILER_PLATFORM_LOG_CONT), "Platform, Log & Container", "")), + (_PL_consts.PSPCF.TRAILER_LIVESTOCK, (str(_PL_consts.PSPCF.TRAILER_LIVESTOCK), "Livestock", "")), + (_PL_consts.PSPCF.TRAILER_LOG, (str(_PL_consts.PSPCF.TRAILER_LOG), "Log Trailer", "")), + ]) + locator_prefab_custom_rule: EnumProperty( + name="Trailer Type", + description="Trailer type", + options={'HIDDEN'}, + items=enum_custom_rule_items.values(), + default=str(_PL_consts.PSPCF.TRAILER_ANY), + ) # LOCATORS - PREFAB - TRAFFIC LIGHTS (SEMAPHORES) enum_tsem_id_items = OrderedDict([(-1, ('-1', "None", ""))]) for i in range(_PL_consts.TSEM_COUNT_MAX): diff --git a/addon/io_scs_tools/shader_presets.txt b/addon/io_scs_tools/shader_presets.txt index e50e68b1..7e8a16b2 100644 --- a/addon/io_scs_tools/shader_presets.txt +++ b/addon/io_scs_tools/shader_presets.txt @@ -2,13 +2,162 @@ Header { FormatVersion: 1 Type: "Presets" Note: "Shader Presets for SCS Blender Tools" - Author: "Simon Lusenc (50keda)" + Author: "Simon Lusenc (50keda), Michaleczeq" Version: 2 Date: "2017-10-03" } +Shader { + PresetName: "baked" + Effect: "eut2.baked" + Flavors: ( "NMAP_TS" "SHADOW" "DAY_MASK" ) + Flags: 0 + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } +} +Shader { + PresetName: "baked.spec" + Effect: "eut2.baked.spec" + Flavors: ( "NMAP_TS" "SHADOW" "DAY_MASK" ) + Flags: 0 + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_over" + FriendlyTag: "Specular map" + Value: "" + TexCoord: ( 0 ) + } +} +Shader { + PresetName: "baked.spec.add.env" + Effect: "eut2.baked.spec.add.env" + Flavors: ( "NMAP_TS" "PAINT_MASK" "SHADOW" "DAY_MASK" ) + Flags: 0 + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.2 0.9 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_over" + FriendlyTag: "Specular map" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + FriendlyTag: "Env factor map" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/vehicle_reflection" + TexCoord: ( -1 ) + } +} +Shader { + PresetName: "billboard" + Effect: "eut2.billboard" + Flavors: ( "LIT" "NMAP_TS_CALC" "SHADOW" "ASAFEW" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 0.0 0.0 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 4.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Mapping { + Tag: "vertex_pos" + FriendlyTag: "Vertex Shift" + TexCoord: ( 1 ) + } +} Shader { PresetName: "building.add.env.day" Effect: "eut2.building.add.env.day" + Flavors: ( "SHADOW" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 0.9 0.9 0.9 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 60.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.9 0.9 0.9 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.2 0.9 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 0.0 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/building_reflection/building_ref" + TexCoord: ( -1 ) + } +} +Shader { + PresetName: "building.add.env.lvcol.day" + Effect: "eut2.building.add.env.lvcol.day" Flags: 0 Attribute { Format: FLOAT3 @@ -43,7 +192,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -60,6 +209,7 @@ Shader { Shader { PresetName: "building.day" Effect: "eut2.building.day" + Flavors: ( "SHADOW" ) Flags: 0 Attribute { Format: FLOAT3 @@ -84,7 +234,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -120,7 +270,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -156,7 +306,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -190,10 +340,10 @@ Shader { Value: ( 0.0 ) } Attribute { - Format: FLOAT + Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Boost" - Value: ( 0.0 ) + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 0.0 0.0 ) } Texture { Tag: "texture[X]:texture_base" @@ -207,6 +357,7 @@ Shader { Flags: 0 Texture { Tag: "texture[X]:texture_base" + FriendlyTag: "Shadow (8bit)" Value: "" TexCoord: ( 0 ) } @@ -214,7 +365,7 @@ Shader { Shader { PresetName: "dif" Effect: "eut2.dif" - Flavors: ( "FLAT" "SHADOW" "PAINT" "TEXGEN0" "TEXMTX0" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "FLAT" "SHADOW" "PAINT" "TEXGEN0" "TEXMTX0" "ALPHA" "DEPTH" "BLEND_ATTR|BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -251,7 +402,7 @@ Shader { PresetName: "dif.anim" Effect: "eut2.dif.anim" # NOTE: intentionally disabled TEXGEN0 flavor as it conflicts with usage of aux[0] attribute - Flavors: ( "FADESHEET|FLIPSHEET" "ALPHA" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "NMAP_TS_CALC_OVER0" "FADESHEET|FLIPSHEET" "ALPHA" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -328,7 +479,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -340,7 +491,7 @@ Shader { Shader { PresetName: "dif.lum.spec" Effect: "eut2.dif.lum.spec" - Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "LUMMULVCOL" "ASAFEW" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "LUMMULVCOL" "ASAFEW" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -370,7 +521,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -382,7 +533,7 @@ Shader { Shader { PresetName: "dif.spec" Effect: "eut2.dif.spec" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "ASAFEW" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "ASAFEW" "ALPHA" "DEPTH" "BLEND_ATTR|BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -418,7 +569,7 @@ Shader { Shader { PresetName: "dif.spec.add.env" Effect: "eut2.dif.spec.add.env" - Flavors: ( "INDENV" "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "INVERTOPAC" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "INDENV" "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "INVERTOPAC" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -469,7 +620,7 @@ Shader { Shader { PresetName: "dif.spec.add.env.nofresnel" Effect: "eut2.dif.spec.add.env.nofresnel" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -513,9 +664,9 @@ Shader { } } Shader { - PresetName: "dif.spec.fade.dif.spec" - Effect: "eut2.dif.spec.fade.dif.spec" - Flavors: ( "NMAP_DETAIL|NMAP_UV_DETAIL" "SHADOW" "ALPHA" ) + PresetName: "dif.spec.add.env.over.dif.opac" + Effect: "eut2.dif.spec.add.env.over.dif.opac" + Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -525,12 +676,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.0 0.0 0.0 ) + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT - Tag: "reflection" - Value: ( 0.0 ) + Tag: "shininess" + Value: ( 60.0 ) } Attribute { Format: FLOAT @@ -539,14 +690,23 @@ Shader { } Attribute { Format: FLOAT - Tag: "shininess" - Value: ( 4.0 ) + Tag: "reflection" + Value: ( 0.0 ) } Attribute { - Format: FLOAT4 - Tag: "aux[5]" - FriendlyTag: "Detail Setup (From, Range, Bias, UV Scale)" - Value: ( 20.0 50.0 0.7 5.0 ) + Format: FLOAT + Tag: "reflection2" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.5 0.5 0.5 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.2 0.9 ) } Texture { Tag: "texture[X]:texture_base" @@ -554,15 +714,20 @@ Shader { TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_detail" + Tag: "texture[X]:texture_over" Value: "" - TexCoord: ( 0 ) + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/vehicle_reflection" + TexCoord: ( -1 ) } } Shader { - PresetName: "dif.spec.mult.dif.spec" - Effect: "eut2.dif.spec.mult.dif.spec" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) + PresetName: "dif.spec.amod.dif.spec" + Effect: "eut2.dif.spec.amod.dif.spec" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "ASAFEWA" ) Flags: 0 Attribute { Format: FLOAT3 @@ -572,12 +737,18 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.400000006 0.400000006 0.400000006 ) + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[0]" + FriendlyTag: "Decal blending factors" + Value: ( 1.0 1.0 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 25.0 ) + Value: ( 20.0 ) } Attribute { Format: FLOAT @@ -587,7 +758,7 @@ Shader { Attribute { Format: FLOAT Tag: "reflection" - Value: ( 0.0 ) + Value: ( 1.0 ) } Texture { Tag: "texture[X]:texture_base" @@ -595,15 +766,22 @@ Shader { TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_mult" + Tag: "texture[X]:texture_mask" + FriendlyTag: "Decal strength" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_over" + FriendlyTag: "Decal" Value: "" TexCoord: ( 1 ) } } Shader { - PresetName: "dif.spec.mult.dif.spec.add.env" - Effect: "eut2.dif.spec.mult.dif.spec.add.env" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) + PresetName: "dif.spec.amod.dif.spec.add.env" + Effect: "eut2.dif.spec.amod.dif.spec.add.env" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "ASAFEWA" ) Flags: 0 Attribute { Format: FLOAT3 @@ -613,12 +791,18 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.400000006 0.400000006 0.400000006 ) + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[0]" + FriendlyTag: "Decal blending factors" + Value: ( 1.0 1.0 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 25.0 ) + Value: ( 20.0 ) } Attribute { Format: FLOAT @@ -628,12 +812,12 @@ Shader { Attribute { Format: FLOAT Tag: "reflection" - Value: ( 0.0 ) + Value: ( 1.0 ) } Attribute { Format: FLOAT3 Tag: "env_factor" - Value: ( 0.5 0.5 0.5 ) + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT2 @@ -646,20 +830,27 @@ Shader { TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_mult" + Tag: "texture[X]:texture_mask" + FriendlyTag: "Decal strength" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_over" + FriendlyTag: "Decal" Value: "" TexCoord: ( 1 ) } Texture { Tag: "texture[X]:texture_reflection" - Value: "/material/environment/vehicle_reflection" + Value: "/material/environment/building_reflection/building_ref" TexCoord: ( -1 ) } } Shader { - PresetName: "dif.spec.mult.dif.spec.iamod.dif.spec" - Effect: "eut2.dif.spec.mult.dif.spec.iamod.dif.spec" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" ) + PresetName: "dif.spec.fade.dif.spec" + Effect: "eut2.dif.spec.fade.dif.spec" + Flavors: ( "NMAP_DETAIL|NMAP_UV_DETAIL" "SHADOW" "ALPHA" ) Flags: 0 Attribute { Format: FLOAT3 @@ -669,12 +860,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 1.0 1.0 1.0 ) + Value: ( 0.0 0.0 0.0 ) } Attribute { Format: FLOAT - Tag: "shininess" - Value: ( 60.0 ) + Tag: "reflection" + Value: ( 0.0 ) } Attribute { Format: FLOAT @@ -683,8 +874,14 @@ Shader { } Attribute { Format: FLOAT - Tag: "reflection" - Value: ( 0.0 ) + Tag: "shininess" + Value: ( 4.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[5]" + FriendlyTag: "Detail Setup (From, Range, Bias, UV Scale)" + Value: ( 20.0 50.0 0.7 5.0 ) } Texture { Tag: "texture[X]:texture_base" @@ -692,20 +889,15 @@ Shader { TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_mult" - Value: "" - TexCoord: ( 1 ) - } - Texture { - Tag: "texture[X]:texture_iamod" + Tag: "texture[X]:texture_detail" Value: "" - TexCoord: ( 2 ) + TexCoord: ( 0 ) } } Shader { - PresetName: "dif.spec.oclu" - Effect: "eut2.dif.spec.oclu" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) + PresetName: "dif.spec.mult.dif.iamod.dif.add.env" + Effect: "eut2.dif.spec.mult.dif.iamod.dif.add.env" + Flavors: ( "NMAP_TS|NMAP_TS_UV" ) Flags: 0 Attribute { Format: FLOAT3 @@ -720,7 +912,7 @@ Shader { Attribute { Format: FLOAT Tag: "shininess" - Value: ( 20.0 ) + Value: ( 60.0 ) } Attribute { Format: FLOAT @@ -730,23 +922,43 @@ Shader { Attribute { Format: FLOAT Tag: "reflection" - Value: ( 1.0 ) - } - Texture { - Tag: "texture[X]:texture_base" - Value: "" - TexCoord: ( 0 ) + Value: ( 0.0 ) } - Texture { - Tag: "texture[X]:texture_oclu" + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.2 0.9 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.8000000119 0.8000000119 0.8000000119 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mult" Value: "" TexCoord: ( 1 ) } + Texture { + Tag: "texture[X]:texture_iamod" + Value: "" + TexCoord: ( 2 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/building_reflection/building_ref" + TexCoord: ( -1 ) + } } Shader { - PresetName: "dif.spec.oclu.add.env" - Effect: "eut2.dif.spec.oclu.add.env" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) + PresetName: "dif.spec.mult.dif.spec" + Effect: "eut2.dif.spec.mult.dif.spec" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "OPASRC01" "DEPTH" "BLEND_ATTR|BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -756,12 +968,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 1.0 1.0 1.0 ) + Value: ( 0.400000006 0.400000006 0.400000006 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 20.0 ) + Value: ( 25.0 ) } Attribute { Format: FLOAT @@ -773,36 +985,21 @@ Shader { Tag: "reflection" Value: ( 0.0 ) } - Attribute { - Format: FLOAT2 - Tag: "fresnel" - Value: ( 0.2 0.9 ) - } - Attribute { - Format: FLOAT3 - Tag: "env_factor" - Value: ( 0.8000000119 0.8000000119 0.8000000119 ) - } Texture { Tag: "texture[X]:texture_base" Value: "" TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_oclu" + Tag: "texture[X]:texture_mult" Value: "" TexCoord: ( 1 ) } - Texture { - Tag: "texture[X]:texture_reflection" - Value: "/material/environment/vehicle_reflection" - TexCoord: ( -1 ) - } } Shader { - PresetName: "dif.spec.oclu.weight.add.env" - Effect: "eut2.dif.spec.oclu.weight.add.env" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" ) + PresetName: "dif.spec.mult.dif.spec.add.env" + Effect: "eut2.dif.spec.mult.dif.spec.add.env" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "OPASRC01" "DEPTH" "BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -812,12 +1009,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 1.0 1.0 1.0 ) + Value: ( 0.400000006 0.400000006 0.400000006 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 20.0 ) + Value: ( 25.0 ) } Attribute { Format: FLOAT @@ -829,23 +1026,23 @@ Shader { Tag: "reflection" Value: ( 0.0 ) } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.5 0.5 0.5 ) + } Attribute { Format: FLOAT2 Tag: "fresnel" Value: ( 0.2 0.9 ) } - Attribute { - Format: FLOAT3 - Tag: "env_factor" - Value: ( 0.8000000119 0.8000000119 0.8000000119 ) - } Texture { Tag: "texture[X]:texture_base" Value: "" TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_oclu" + Tag: "texture[X]:texture_mult" Value: "" TexCoord: ( 1 ) } @@ -856,9 +1053,9 @@ Shader { } } Shader { - PresetName: "dif.spec.over.dif.opac" - Effect: "eut2.dif.spec.over.dif.opac" - Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) + PresetName: "dif.spec.mult.dif.spec.iamod.dif.spec" + Effect: "eut2.dif.spec.mult.dif.spec.iamod.dif.spec" + Flavors: ( "NMAP_TS|NMAP_TS_UV" ) Flags: 0 Attribute { Format: FLOAT3 @@ -868,12 +1065,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 1.0, 1.0, 1.0 ) + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 24.0 ) + Value: ( 60.0 ) } Attribute { Format: FLOAT @@ -885,26 +1082,26 @@ Shader { Tag: "reflection" Value: ( 0.0 ) } - Attribute { - Format: FLOAT - Tag: "reflection2" - Value: ( 0.0 ) - } Texture { Tag: "texture[X]:texture_base" Value: "" TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_over" + Tag: "texture[X]:texture_mult" Value: "" TexCoord: ( 1 ) } + Texture { + Tag: "texture[X]:texture_iamod" + Value: "" + TexCoord: ( 2 ) + } } Shader { - PresetName: "dif.spec.weight" - Effect: "eut2.dif.spec.weight" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "ASAFEW" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + PresetName: "dif.spec.oclu" + Effect: "eut2.dif.spec.oclu" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_ATTR|BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -929,23 +1126,28 @@ Shader { Attribute { Format: FLOAT Tag: "reflection" - Value: ( 0.0 ) + Value: ( 1.0 ) } Texture { Tag: "texture[X]:texture_base" Value: "" TexCoord: ( 0 ) } + Texture { + Tag: "texture[X]:texture_oclu" + Value: "" + TexCoord: ( 1 ) + } } Shader { - PresetName: "dif.spec.weight.add.env" - Effect: "eut2.dif.spec.weight.add.env" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "PAINT" "TEXGEN0" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + PresetName: "dif.spec.oclu.add.env" + Effect: "eut2.dif.spec.oclu.add.env" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 Tag: "diffuse" - Value: ( 0.5 0.5 0.5 ) + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT3 @@ -955,7 +1157,7 @@ Shader { Attribute { Format: FLOAT Tag: "shininess" - Value: ( 15.0 ) + Value: ( 20.0 ) } Attribute { Format: FLOAT @@ -967,31 +1169,36 @@ Shader { Tag: "reflection" Value: ( 0.0 ) } - Attribute { - Format: FLOAT3 - Tag: "env_factor" - Value: ( 0.4 0.4 0.4 ) - } Attribute { Format: FLOAT2 Tag: "fresnel" Value: ( 0.2 0.9 ) } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.8000000119 0.8000000119 0.8000000119 ) + } Texture { Tag: "texture[X]:texture_base" Value: "" TexCoord: ( 0 ) } + Texture { + Tag: "texture[X]:texture_oclu" + Value: "" + TexCoord: ( 1 ) + } Texture { Tag: "texture[X]:texture_reflection" - Value: "/material/environment/generic_reflection" + Value: "/material/environment/vehicle_reflection" TexCoord: ( -1 ) } } Shader { - PresetName: "dif.spec.weight.mult2" - Effect: "eut2.dif.spec.weight.mult2" - Flavors: ( "SHADOW" "TEXGEN0" "ALPHA" ) + PresetName: "dif.spec.oclu.weight.add.env" + Effect: "eut2.dif.spec.oclu.weight.add.env" + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "PAINT" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1006,7 +1213,7 @@ Shader { Attribute { Format: FLOAT Tag: "shininess" - Value: ( 65.0 ) + Value: ( 20.0 ) } Attribute { Format: FLOAT @@ -1016,13 +1223,17 @@ Shader { Attribute { Format: FLOAT Tag: "reflection" - Value: ( 7.0 ) + Value: ( 0.0 ) } Attribute { Format: FLOAT2 - Tag: "aux[5]" - FriendlyTag: "Mult UV Scale" - Value: ( 4.4 12.0 ) + Tag: "fresnel" + Value: ( 0.2 0.9 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.8000000119 0.8000000119 0.8000000119 ) } Texture { Tag: "texture[X]:texture_base" @@ -1030,15 +1241,20 @@ Shader { TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_mult" + Tag: "texture[X]:texture_oclu" Value: "" - TexCoord: ( 0 ) + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/vehicle_reflection" + TexCoord: ( -1 ) } } Shader { - PresetName: "dif.spec.weight.mult2.weight2" - Effect: "eut2.dif.spec.weight.mult2.weight2" - Flavors: ( "SHADOW" "TEXGEN0" "TEXGEN1" ) + PresetName: "dif.spec.over.dif.opac" + Effect: "eut2.dif.spec.over.dif.opac" + Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_ATTR|BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1048,12 +1264,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 1.0 1.0 1.0 ) + Value: ( 1.0, 1.0, 1.0 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 65.0 ) + Value: ( 24.0 ) } Attribute { Format: FLOAT @@ -1063,24 +1279,12 @@ Shader { Attribute { Format: FLOAT Tag: "reflection" - Value: ( 7.0 ) + Value: ( 0.0 ) } Attribute { Format: FLOAT Tag: "reflection2" - Value: ( 1.3 ) - } - Attribute { - Format: FLOAT4 - Tag: "aux[3]" - FriendlyTag: "Specular2 & Shininess2" - Value: ( 1.0 1.0 1.0 40.0 ) - } - Attribute { - Format: FLOAT4 - Tag: "aux[5]" - FriendlyTag: "Mult & Mult_1 UV Scale" - Value: ( 4.4 12.0 4.4 12.0 ) + Value: ( 0.0 ) } Texture { Tag: "texture[X]:texture_base" @@ -1088,25 +1292,15 @@ Shader { TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_mult" - Value: "" - TexCoord: ( 0 ) - } - Texture { - Tag: "texture[X]:texture_base_1" - Value: "" - TexCoord: ( 1 ) - } - Texture { - Tag: "texture[X]:texture_mult_1" + Tag: "texture[X]:texture_over" Value: "" TexCoord: ( 1 ) } } Shader { - PresetName: "dif.spec.weight.weight.dif.spec.weight" - Effect: "eut2.dif.spec.weight.weight.dif.spec.weight" - Flavors: ( "SHADOW" "TEXGEN0" "TEXGEN1" "ALPHA" ) + PresetName: "dif.spec.over.dif.opac.add.env" + Effect: "eut2.dif.spec.over.dif.opac.add.env" + Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1116,12 +1310,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.200000003 0.200000003 0.200000003 ) + Value: ( 1.0, 1.0, 1.0 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 12.0 ) + Value: ( 24.0 ) } Attribute { Format: FLOAT @@ -1133,17 +1327,21 @@ Shader { Tag: "reflection" Value: ( 0.0 ) } - Attribute { - Format: FLOAT4 - Tag: "aux[3]" - FriendlyTag: "Specular2 & Shininess2" - Value: ( 0.1000000015 0.1000000015 0.1000000015 12.0 ) - } Attribute { Format: FLOAT Tag: "reflection2" Value: ( 0.0 ) } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.5 0.5 0.5 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.2 0.9 ) + } Texture { Tag: "texture[X]:texture_base" Value: "" @@ -1156,9 +1354,9 @@ Shader { } } Shader { - PresetName: "dif.weight.dif" - Effect: "eut2.dif.weight.dif" - Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + PresetName: "dif.spec.weight" + Effect: "eut2.dif.spec.weight" + Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_CALC" "SHADOW" "PAINT" "TEXGEN0" "ASAFEW" "ALPHA" "DEPTH" "HMAP" "BLEND_ATTR|BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1168,7 +1366,7 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.05 0.05 0.05 ) + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT @@ -1178,16 +1376,11 @@ Shader { Attribute { Format: FLOAT Tag: "add_ambient" - Value: ( 1.0 ) - } - Attribute { - Format: FLOAT - Tag: "reflection" Value: ( 0.0 ) } Attribute { Format: FLOAT - Tag: "reflection2" + Tag: "reflection" Value: ( 0.0 ) } Texture { @@ -1195,47 +1388,670 @@ Shader { Value: "" TexCoord: ( 0 ) } - Texture { - Tag: "texture[X]:texture_over" - Value: "" - TexCoord: ( 1 ) - } } Shader { - PresetName: "fakeshadow" - Effect: "eut2.fakeshadow" - Flavors: ( "NOCULL" ) + PresetName: "dif.spec.weight.add.env" + Effect: "eut2.dif.spec.weight.add.env" + Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_UV_2" "SHADOW" "PAINT" "TEXGEN0" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { - Format: FLOAT - Tag: "shadow_bias" - Value: ( 0.0 ) + Format: FLOAT3 + Tag: "diffuse" + Value: ( 0.5 0.5 0.5 ) } -} -Shader { - PresetName: "flare" - Effect: "eut2.flare" - Flags: 0 Attribute { Format: FLOAT3 - Tag: "diffuse" + Tag: "specular" Value: ( 1.0 1.0 1.0 ) } Attribute { - Format: FLOAT2 - Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" - Value: ( 0.0 0.0 ) - } + Format: FLOAT + Tag: "shininess" + Value: ( 15.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.4 0.4 0.4 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.2 0.9 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/generic_reflection" + TexCoord: ( -1 ) + } +} +Shader { + PresetName: "dif.spec.weight.add.env.nofresnel" + Effect: "eut2.dif.spec.weight.add.env.nofresnel" + Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "ALPHA" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 0.5 0.5 0.5 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 15.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.4 0.4 0.4 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/generic_reflection" + TexCoord: ( -1 ) + } +} +Shader { + PresetName: "dif.spec.weight.mask.dif.spec.weight" + Effect: "eut2.dif.spec.weight.mask.dif.spec.weight" + Flavors: ( "SHADOW" "PAINT" "DEPTH" "BLEND_ATTR" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 20.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[3]" + FriendlyTag: "Specular2 & Shininess2" + Value: ( 1.0 1.0 1.0 20.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection2" + Value: ( 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_over" + Value: "" + TexCoord: ( 2 ) + } +} +Shader { + PresetName: "dif.spec.weight.mult2" + Effect: "eut2.dif.spec.weight.mult2" + Flavors: ( "SHADOW" "TEXGEN0" "ALPHA" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 65.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 7.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Mult UV Scale" + Value: ( 4.4 12.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mult" + Value: "" + TexCoord: ( 0 ) + } +} +Shader { + PresetName: "dif.spec.weight.mult2.mask2" + Effect: "eut2.dif.spec.weight.mult2.mask2" + Flavors: ( "SHADOW" "TEXGEN0" "TEXGEN1" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 65.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 7.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection2" + Value: ( 1.3 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[3]" + FriendlyTag: "Specular2 & Shininess2" + Value: ( 1.0 1.0 1.0 40.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[5]" + FriendlyTag: "Mult & Mult_1 UV Scale" + Value: ( 4.4 12.0 4.4 12.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mult" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_base_1" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_mult_1" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_mask" + Value: "" + TexCoord: ( 2 ) + } +} +Shader { + PresetName: "dif.spec.weight.mult2.weight2" + Effect: "eut2.dif.spec.weight.mult2.weight2" + Flavors: ( "SHADOW" "TEXGEN0" "TEXGEN1" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 65.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 7.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection2" + Value: ( 1.3 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[3]" + FriendlyTag: "Specular2 & Shininess2" + Value: ( 1.0 1.0 1.0 40.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[5]" + FriendlyTag: "Mult & Mult_1 UV Scale" + Value: ( 4.4 12.0 4.4 12.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mult" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_base_1" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_mult_1" + Value: "" + TexCoord: ( 1 ) + } +} +Shader { + PresetName: "dif.spec.weight.weight.dif.spec.weight" + Effect: "eut2.dif.spec.weight.weight.dif.spec.weight" + Flavors: ( "NMAP_TS_CALC_OVER" "SHADOW" "TEXGEN0" "TEXGEN1" "ALPHA" "HMAP_OVER" "CALIBRATED" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + # NOTE: This shader use new secondary diffuse that is not supported in old material system by conversion tool 2.20 yet. + # To prevent being , this attribute will be removed on import automatically. If newer version of conversion tool will support it, this should be changed. + # This attribute will be probably named "aux[4]" in future. + # Attribute { + # Format: FLOAT3 + # Tag: "diffuse_secondary" + # Value: ( 1.0 1.0 1.0 ) + # } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 0.200000003 0.200000003 0.200000003 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 12.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[3]" + FriendlyTag: "Specular2 & Shininess2" + Value: ( 0.1000000015 0.1000000015 0.1000000015 12.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection2" + Value: ( 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_over" + Value: "" + TexCoord: ( 1 ) + } +} +Shader { + PresetName: "dif.weight.dif" + Effect: "eut2.dif.weight.dif" + Flavors: ( "SHADOW" "PAINT" "TEXGEN0" "TEXGEN1" "ALPHA" "DEPTH" "BLEND_ATTR|BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 0.05 0.05 0.05 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 20.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "reflection2" + Value: ( 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_over" + Value: "" + TexCoord: ( 1 ) + } +} +Shader { + PresetName: "fakeshadow" + Effect: "eut2.fakeshadow" + Flavors: ( "ALPHA_MASK" "NOCULL" ) + Flags: 0 + Attribute { + Format: FLOAT + Tag: "shadow_bias" + Value: ( 0.0 ) + } +} +Shader { + PresetName: "flare" + Effect: "eut2.flare" + Flavors: ( "SUN_BOUNCE|SUN_DIRECT|SUN_LUCENT" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 0.0 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "/model/flare/flare" + TexCoord: ( 0 ) + } +} +Shader { + PresetName: "glass" + Effect: "eut2.glass" + Flavors: ( "NMAP_TS" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 25.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.01 -1 ) + } + Attribute { + Format: FLOAT3 + Tag: "tint" + Value: ( 0.97 0.97 0.97 ) + } + Attribute { + Format: FLOAT + Tag: "tint_opacity" + Value: ( 0.0 ) + } + Attribute { + Format: INT + Tag: "queue_bias" + Value: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "/vehicle/truck/share/glass_ex" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/vehicle_reflection" + TexCoord: ( -1 ) + } +} +Shader { + PresetName: "grass" + Effect: "eut2.grass" + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 0.0 0.0 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 4.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } +} +Shader { + PresetName: "interior.lit" + Effect: "eut2.interior.lit" + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT + Tag: "shininess" + Value: ( 65.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.200000003 0.8999999762 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 1700.0 17.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[1]" + FriendlyTag: "Atlas Dimensions (X,Z)" + Value: ( 8.0 4.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[2]" + FriendlyTag: "Glass Color (R,G,B,Factor)" + Value: ( 1.0 1.0 1.0 0.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[0]" + FriendlyTag: "Room Dimensions (X,Z)" + Value: ( 3.5 3.5 ) + } Texture { Tag: "texture[X]:texture_base" - Value: "/model/flare/flare" + FriendlyTag: "Atlas" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_layer0" + FriendlyTag: "Emissive (Day)" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_layer1" + FriendlyTag: "Emissive (Night)" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + FriendlyTag: "LUT" + Value: "" TexCoord: ( 0 ) } + Texture { + Tag: "texture[X]:texture_nmap_detail" + FriendlyTag: "Normal Map" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/building_reflection/building_ref" + TexCoord: ( -1 ) + } + Mapping { + Tag: "perturbation" + TexCoord: ( 1 ) + } } Shader { - PresetName: "glass" - Effect: "eut2.glass" + PresetName: "interior.curtain.lit" + Effect: "eut2.interior.curtain.lit" Flags: 0 Attribute { Format: FLOAT3 @@ -1250,47 +2066,185 @@ Shader { Attribute { Format: FLOAT Tag: "shininess" - Value: ( 25.0 ) + Value: ( 65.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "fresnel" + Value: ( 0.200000003 0.8999999762 ) + } + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 1700.0 17.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[1]" + FriendlyTag: "Atlas Dimensions (X,Z)" + Value: ( 8.0 4.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[2]" + FriendlyTag: "Glass Color (R,G,B,Factor)" + Value: ( 1.0 1.0 1.0 0.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[0]" + FriendlyTag: "Room Dimensions (X,Z)" + Value: ( 3.5 3.5 ) + } + Texture { + Tag: "texture[X]:texture_base" + FriendlyTag: "Atlas" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_over" + FriendlyTag: "Curtains" + Value: "" + TexCoord: ( 2 ) + } + Texture { + Tag: "texture[X]:texture_layer0" + FriendlyTag: "Emissive (Day)" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_layer1" + FriendlyTag: "Emissive (Night)" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + FriendlyTag: "LUT" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_nmap_detail" + FriendlyTag: "Normal Map" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/building_reflection/building_ref" + TexCoord: ( -1 ) + } + Mapping { + Tag: "perturbation" + TexCoord: ( 1 ) + } +} +Shader { + PresetName: "interior.spatial.lit" + Effect: "eut2.interior.spatial.lit" + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "specular" + Value: ( 1.0 1.0 1.0 ) } Attribute { Format: FLOAT - Tag: "add_ambient" - Value: ( 0.0 ) + Tag: "shininess" + Value: ( 65.0 ) } Attribute { Format: FLOAT2 Tag: "fresnel" - Value: ( 0.01 -1 ) + Value: ( 0.200000003 0.8999999762 ) } Attribute { Format: FLOAT3 - Tag: "tint" - Value: ( 0.97 0.97 0.97 ) + Tag: "env_factor" + Value: ( 1.0 1.0 1.0 ) } Attribute { - Format: FLOAT - Tag: "tint_opacity" - Value: ( 0.0 ) + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 1700.0 17.0 ) } Attribute { - Format: INT - Tag: "queue_bias" - Value: ( 0 ) + Format: FLOAT2 + Tag: "aux[1]" + FriendlyTag: "Atlas Dimensions (X,Z)" + Value: ( 8.0 4.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[2]" + FriendlyTag: "Glass Color (R,G,B,Factor)" + Value: ( 1.0 1.0 1.0 0.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[0]" + FriendlyTag: "Room Dimensions (X,Z)" + Value: ( 3.5 3.5 ) } Texture { Tag: "texture[X]:texture_base" - Value: "/vehicle/truck/share/glass_ex" + FriendlyTag: "Atlas" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_layer0" + FriendlyTag: "Emissive (Day)" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_layer1" + FriendlyTag: "Emissive (Night)" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + FriendlyTag: "LUT" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_nmap_detail" + FriendlyTag: "Normal Map" + Value: "" TexCoord: ( 0 ) } Texture { Tag: "texture[X]:texture_reflection" - Value: "/material/environment/vehicle_reflection" + Value: "/material/environment/building_reflection/building_ref" TexCoord: ( -1 ) } + Mapping { + Tag: "perturbation" + TexCoord: ( 1 ) + } } Shader { - PresetName: "grass" - Effect: "eut2.grass" + PresetName: "lamp" + Effect: "eut2.lamp" + Flavors: ( "LAMP_ANIM" "NMAP_TS|NMAP_TS_UV" "SHADOW" "ENVMAP" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1300,12 +2254,12 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.0 0.0 0.0 ) + Value: ( 0.6999999881 0.6999999881 0.6999999881 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 4.0 ) + Value: ( 90.0 ) } Attribute { Format: FLOAT @@ -1317,11 +2271,17 @@ Shader { Value: "" TexCoord: ( 0 ) } + Texture { + Tag: "texture[X]:texture_mask" + FriendlyTag: "Lamp Mask" + Value: "" + TexCoord: ( 1 ) + } } Shader { - PresetName: "lamp" - Effect: "eut2.lamp" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "ENVMAP" "SHADOW" ) + PresetName: "leaves" + Effect: "eut2.leaves" + Flavors: ( "NMAP_TS_CALC" "SHADOW" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1331,32 +2291,80 @@ Shader { Attribute { Format: FLOAT3 Tag: "specular" - Value: ( 0.6999999881 0.6999999881 0.6999999881 ) + Value: ( 0.0 0.0 0.0 ) } Attribute { Format: FLOAT Tag: "shininess" - Value: ( 90.0 ) + Value: ( 4.0 ) + } + Attribute { + Format: FLOAT + Tag: "add_ambient" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT + Tag: "aux[0]" + FriendlyTag: "Shadow Offset" + Value: ( 0.0 ) + } + Attribute { + Format: FLOAT3 + Tag: "aux[1]" + FriendlyTag: "LOD Selector" + Value: ( 0.0 1.0 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + Value: "" + TexCoord: ( 0 ) + } + Mapping { + Tag: "vertex_pos" + FriendlyTag: "Vertex Shift" + TexCoord: ( 1 ) + } + Mapping { + Tag: "unknown" + TexCoord: ( 2 ) + } +} +Shader { + PresetName: "light" + Effect: "eut2.light" + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) } Attribute { - Format: FLOAT - Tag: "add_ambient" - Value: ( 0.0 ) + Format: FLOAT3 + Tag: "specular" + Value: ( 0.0 0.0 0.0 ) } - Texture { - Tag: "texture[X]:texture_base" - Value: "" - TexCoord: ( 0 ) + Attribute { + Format: FLOAT3 + Tag: "env_factor" + Value: ( 0.0 0.0 0.0 ) } - Texture { - Tag: "texture[X]:texture_mask" - Value: "" - TexCoord: ( 1 ) + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 0.0 0.0 ) } } Shader { PresetName: "light.tex" Effect: "eut2.light.tex" + Flavors: ( "LSBOUNCE|LSDIRECT|LSLUCENT" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1382,11 +2390,12 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { Tag: "texture[X]:texture_base" + FriendlyTag: "Mask" Value: "" TexCoord: ( 0 ) } @@ -1419,15 +2428,17 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { Tag: "texture[X]:texture_base" + FriendlyTag: "Mask" Value: "" TexCoord: ( 0 ) } } +# NOTE: TODO: mix00 shaders (can't recreate it cause it's not used anywhere and it's unique) Shader { PresetName: "mlaaweight" Effect: "eut2.mlaaweight" @@ -1439,6 +2450,28 @@ Shader { Effect: "eut2.none" Flags: 0 } +Shader { + PresetName: "particle" + Effect: "eut2.particle" + Flavors: ( "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flags: 0 + Attribute { + Format: FLOAT3 + Tag: "diffuse" + Value: ( 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[5]" + FriendlyTag: "Luminance (Output, Night) (nit)" + Value: ( 0.0 0.0 ) + } + Texture { + Tag: "texture[X]:texture_base" + Value: "" + TexCoord: ( 0 ) + } +} Shader { PresetName: "reflective" Effect: "eut2.reflective" @@ -1452,7 +2485,7 @@ Shader { Shader { PresetName: "retroreflective" Effect: "eut2.retroreflective" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "RETROREFLECTIVE_DIM_ALLDIR|RETROREFLECTIVE_PIKO_ALLDIR" "RETROREFLECTIVE_DECAL" ) + Flavors: ( "NMAP_TS|NMAP_TS_UV" "RETROREFLECTIVE_DIM_ALLDIR|RETROREFLECTIVE_PIKO_ALLDIR" "RETROREFLECTIVE_DECAL" ) Flags: 0 Texture { Tag: "texture[X]:texture_base" @@ -1466,6 +2499,7 @@ Shader { Flags: 0 Texture { Tag: "texture[X]:texture_base" + FriendlyTag: "Shadow (8bit)" Value: "" TexCoord: ( 0 ) } @@ -1473,7 +2507,7 @@ Shader { Shader { PresetName: "shadowonly" Effect: "eut2.shadowonly" - Flavors: ( "NOCULL" ) + Flavors: ( "ALPHA_MASK" "NOCULL" ) Flags: 0 Attribute { Format: FLOAT @@ -1514,7 +2548,7 @@ Shader { Shader { PresetName: "sky" Effect: "eut2.sky" - Flavors: ( "SKYPART_BACKGROUND|SKYPART_STARS" ) + Flavors: ( "SKYPART_NOTEXTURE|SKYPART_STARS" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1546,29 +2580,57 @@ Shader { } Texture { Tag: "texture[X]:texture_sky_weather_base_a" - Value: "/model/skybox/nice_00/nice00_08_80_g" + FriendlyTag: "Base A" + Value: "/asset/skybox/s_nice_00/s_nice00_10_80_g" TexCoord: ( 0 ) } Texture { Tag: "texture[X]:texture_sky_weather_base_b" - Value: "/model/skybox/nice_00/nice00_08_80_g" + FriendlyTag: "Base B" + Value: "/asset/skybox/s_nice_00/s_nice00_10_80_g" TexCoord: ( 0 ) } Texture { Tag: "texture[X]:texture_sky_weather_over_a" - Value: "/model/skybox/bad_00/bad00_08_80_g" + FriendlyTag: "Over A" + Value: "/asset/skybox/s_bad_02/s_bad02_03_10_g" TexCoord: ( 0 ) } Texture { Tag: "texture[X]:texture_sky_weather_over_b" - Value: "/model/skybox/nice_00/nice00_08_80_g" + FriendlyTag: "Over B" + Value: "/asset/skybox/s_bad_02/s_bad02_03_10_g" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_sky_weather_base_mask_a" + FriendlyTag: "Mask Base A" + Value: "/asset/skybox/s_nice_05/s_nice05_05_00_c" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_sky_weather_base_mask_b" + FriendlyTag: "Mask Base B" + Value: "/asset/skybox/s_nice_05/s_nice05_05_00_c" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_sky_weather_over_mask_a" + FriendlyTag: "Mask Over A" + Value: "/asset/skybox/s_bad_02/s_bad02_05_00_c" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_sky_weather_over_mask_b" + FriendlyTag: "Mask Over B" + Value: "/asset/skybox/s_bad_02/s_bad02_05_00_c" TexCoord: ( 0 ) } } Shader { PresetName: "truckpaint" Effect: "eut2.truckpaint" - Flavors: ( "NMAP_TS|NMAP_TS_UV|NMAP_TS_16|NMAP_TS_UV_16" "SHADOW" "COLORMASK|AIRBRUSH" "FLIPFLAKE" "ALTUV" ) + Flavors: ( "NMAP_TS|NMAP_TS_UV" "SHADOW" "COLORMASK|AIRBRUSH" "FLIPFLAKE|FLIPFLAKEUV" "ALTUV" "ASAFEWA" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1619,7 +2681,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -1631,7 +2693,7 @@ Shader { Shader { PresetName: "unlit.vcol.tex" Effect: "eut2.unlit.vcol.tex" - Flavors: ( "FADESHEET|FLIPSHEET" "PAINT" "ALPHA" "AWHITE" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) + Flavors: ( "FADESHEET|FLIPSHEET" "LSBOUNCE|LSDIRECT|LSLUCENT" "PAINT" "NOZ" "ALPHA" "AWHITE" "DEPTH" "BLEND_OVER|BLEND_MULT|BLEND_ADD" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1641,7 +2703,7 @@ Shader { Attribute { Format: FLOAT2 Tag: "aux[5]" - FriendlyTag: "Luminance Output (Day, Night) (nit)" + FriendlyTag: "Luminance (Output, Night) (nit)" Value: ( 0.0 0.0 ) } Texture { @@ -1653,6 +2715,7 @@ Shader { Shader { PresetName: "water" Effect: "eut2.water" + Flavors: ( "WATER_STREAM" "TOSTREAM|TOWATER" "MIRROR" ) Flags: 0 Attribute { Format: FLOAT3 @@ -1717,16 +2780,19 @@ Shader { } Texture { Tag: "texture[X]:texture_layer0" + FriendlyTag: "Waves 0" Value: "/material/terrain/water/waves_lake" TexCoord: ( -1 ) } Texture { Tag: "texture[X]:texture_layer1" + FriendlyTag: "Waves 1" Value: "/material/terrain/water/waves_lake" TexCoord: ( -1 ) } Texture { Tag: "texture[X]:texture_reflection" + FriendlyTag: "Environment cube" Value: "/material/environment/generic_reflection" TexCoord: ( -1 ) } @@ -1765,16 +2831,16 @@ Shader { Value: "/material/special/window_interior_01" TexCoord: ( 0 ) } - Texture { - Tag: "texture[X]:texture_reflection" - Value: "/material/environment/building_reflection/building_ref" - TexCoord: ( -1 ) - } Texture { Tag: "texture[X]:texture_lightmap" Value: "/material/special/window_interior_01" TexCoord: ( 1 ) } + Texture { + Tag: "texture[X]:texture_reflection" + Value: "/material/environment/building_reflection/building_ref" + TexCoord: ( -1 ) + } } Flavor { Type: "AIRBRUSH" @@ -1795,14 +2861,68 @@ Flavor { Type: "ALPHA" Name: "a" } +Flavor { + Type: "ALPHA_MASK" + Name: "a" + Texture { + Tag: "texture[X]:texture_base" + FriendlyTag: "Alpha source" + Value: "" + TexCoord: ( 0 ) + } +} Flavor { Type: "ALTUV" Name: "altuv" } +Flavor { + Type: "LAMP_ANIM" + Name: "anim" + Attribute { + Format: FLOAT4 + Tag: "aux[0]" + FriendlyTag: "Sheet frame size (R, G)" + Value: ( 1.0 1.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT4 + Tag: "aux[1]" + FriendlyTag: "Sheet frame size (B, A)" + Value: ( 1.0 1.0 1.0 1.0 ) + } + Texture { + Tag: "texture[X]:texture_mask" + FriendlyTag: "Lamp Mask R" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_mask_1" + FriendlyTag: "Lamp Mask G" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_mask_2" + FriendlyTag: "Lamp Mask B" + Value: "" + TexCoord: ( 1 ) + } + Texture { + Tag: "texture[X]:texture_mask_3" + FriendlyTag: "Lamp Mask A" + Value: "" + TexCoord: ( 1 ) + } +} Flavor { Type: "ASAFEW" Name: "asafew" } +Flavor { + Type: "ASAFEWA" + Name: "asafew.a" +} Flavor { Type: "AWHITE" Name: "awhite" @@ -1812,13 +2932,17 @@ Flavor { Name: "add" } Flavor { - Type: "BLEND_OVER" - Name: "over" + Type: "BLEND_ATTR" + Name: "attr" } Flavor { Type: "BLEND_MULT" Name: "mult" } +Flavor { + Type: "BLEND_OVER" + Name: "over" +} Flavor { Type: "COLORMASK" Name: "colormask" @@ -1852,6 +2976,15 @@ Flavor { TexCoord: ( 1 2 ) } } +Flavor { + Type: "DAY_MASK" + Name: "day" + Texture { + Tag: "texture[X]:texture_lightmap" + Value: "" + TexCoord: ( 0 ) + } +} Flavor { Type: "DEPTH" Name: "decal" @@ -1930,58 +3063,160 @@ Flavor { Type: "FLAT" Name: "flat" } +# NOTE: this flavors are supported by shaders, but not supported by conversion tool for now. Commented to not display in Blender. +# Flavor { +# Type: "HMAP" +# Name: "heightmap" +# # NOTE: can't say in which order aux[7] values should be set now (there is no any shader using this flavor now). What's more, conversion tool not support that attribute. +# Attribute { +# Format: FLOAT4 +# Tag: "aux[7]" +# FriendlyTag: "Heightmap (Feather size, Height range, Shape type)" +# Value: ( 0.01 -1.0 1.0 0.0 ) +# } +# Texture { +# Tag: "texture[X]:texture_heightmap" +# FriendlyTag: "Heightmap" +# Value: "" +# TexCoord: ( 0 ) +# } +# } +# Flavor { +# Type: "HMAP_OVER" +# Name: "heightmap" +# # NOTE: can't say in which order aux[7/8] values should be set now (there is no any shader using this flavor now). What's more, conversion tool not support that attribute. +# Attribute { +# Format: FLOAT4 +# Tag: "aux[7]" +# FriendlyTag: "Heightmap (Feather size, Height range, Shape type)" +# Value: ( 0.01 -1.0 1.0 0.0 ) +# } +# Attribute { +# Format: FLOAT4 +# Tag: "aux[8]" +# FriendlyTag: "Heightmap2 (Feather size, Height range, Shape type)" +# Value: ( 0.01 -1.0 1.0 0.0 ) +# } +# Texture { +# Tag: "texture[X]:texture_heightmap" +# FriendlyTag: "Heightmap" +# Value: "" +# TexCoord: ( 0 ) +# } +# Texture { +# Tag: "texture[X]:texture_heightmap_over" +# FriendlyTag: "Heightmap Over" +# Value: "" +# TexCoord: ( 1 ) +# } +# } +# Flavor { +# Type: "CALIBRATED" +# Name: "calibrated" +# } Flavor { Type: "INDENV" Name: "indenv" } +Flavor { + Type: "INVERTOPAC" + Name: "oinv" +} +Flavor { + Type: "LIT" + Name: "lit" + Texture { + Tag: "texture[X]:texture_lightmap" + Value: "" + TexCoord: ( 0 ) + } +} +Flavor { + Type: "LSBOUNCE" + Name: "lsbounce" +} +Flavor { + Type: "LSDIRECT" + Name: "lsdirect" +} +Flavor { + Type: "LSLUCENT" + Name: "lslucent" +} Flavor { Type: "LUMMULVCOL" Name: "lvcol" } +Flavor { + Type: "MIRROR" + Name: "mirror" +} +Flavor { + Type: "NMAP_DETAIL" + Name: "tsnmap" + Texture { + Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_nmap_detail" + FriendlyTag: "Normal map (detail)" + Value: "" + TexCoord: ( 0 ) + } +} Flavor { Type: "NMAP_TS" Name: "tsnmap" Texture { Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" Value: "" TexCoord: ( 0 ) } } Flavor { - Type: "NMAP_TS_16" - Name: "tsnmap16" + Type: "NMAP_TS_CALC" + Name: "tsnmapcalc" Texture { Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" Value: "" TexCoord: ( 0 ) } } Flavor { - Type: "NMAP_DETAIL" - Name: "tsnmap" + Type: "NMAP_TS_CALC_OVER0" + Name: "tsnmapcalc" Texture { Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" Value: "" TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_nmap_detail" + Tag: "texture[X]:texture_nmap_over" + FriendlyTag: "Normal map (over)" Value: "" TexCoord: ( 0 ) } } Flavor { - Type: "NMAP_UV_DETAIL" - Name: "tsnmapuv" + Type: "NMAP_TS_CALC_OVER" + Name: "tsnmapcalc" Texture { Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" Value: "" - TexCoord: ( 6 ) + TexCoord: ( 0 ) } Texture { - Tag: "texture[X]:texture_nmap_detail" + Tag: "texture[X]:texture_nmap_over" + FriendlyTag: "Normal map (over)" Value: "" - TexCoord: ( 6 ) + TexCoord: ( 1 ) } } Flavor { @@ -1989,15 +3224,39 @@ Flavor { Name: "tsnmapuv" Texture { Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" + Value: "" + TexCoord: ( 6 ) + } +} +Flavor { + Type: "NMAP_UV_DETAIL" + Name: "tsnmapuv" + Texture { + Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" + Value: "" + TexCoord: ( 6 ) + } + Texture { + Tag: "texture[X]:texture_nmap_detail" + FriendlyTag: "Normal map (detail)" Value: "" TexCoord: ( 6 ) } } Flavor { - Type: "NMAP_TS_UV_16" - Name: "tsnmapuv16" + Type: "NMAP_TS_UV_2" + Name: "tsnmapuv2" Texture { Tag: "texture[X]:texture_nmap" + FriendlyTag: "Normal map" + Value: "" + TexCoord: ( 0 ) + } + Texture { + Tag: "texture[X]:texture_nmap_detail" + FriendlyTag: "Normal map (hi-freq detail)" Value: "" TexCoord: ( 6 ) } @@ -2006,6 +3265,36 @@ Flavor { Type: "NOCULL" Name: "nocull" } +Flavor { + Type: "OPASRC01" + Name: "opasrc01" +} +Flavor { + Type: "PAINT" + Name: "paint" +} +Flavor { + Type: "PAINT_MASK" + Name: "paint" + Texture { + Tag: "texture[X]:texture_mask_1" + FriendlyTag: "Paint mask" + Value: "" + TexCoord: ( 0 ) + } +} +Flavor { + Type: "RETROREFLECTIVE_DECAL" + Name: "decal.over" +} +Flavor { + Type: "RETROREFLECTIVE_DIM_ALLDIR" + Name: "dim.alldir" +} +Flavor { + Type: "RETROREFLECTIVE_PIKO_ALLDIR" + Name: "piko.alldir" +} Flavor { Type: "SHADOW" Name: "shadow" @@ -2016,8 +3305,8 @@ Flavor { } } Flavor { - Type: "SKYPART_BACKGROUND" - Name: "back" + Type: "SKYPART_NOTEXTURE" + Name: "bottom" Attribute { Format: FLOAT4 Tag: "aux[1]" @@ -2056,38 +3345,82 @@ Flavor { } } Flavor { - Type: "PAINT" - Name: "paint" -} -Flavor { - Type: "RETROREFLECTIVE_DECAL" - Name: "decal.over" + Type: "SUN_BOUNCE" + Name: "sun.bounce" + Attribute { + Format: FLOAT3 + Tag: "aux[0]" + FriendlyTag: "Sheet (playback FPS, frames per row, frames total)" + Value: ( 30.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[1]" + FriendlyTag: "Sheet frame (width, height)" + Value: ( 0.0 0.0 ) + } } Flavor { - Type: "RETROREFLECTIVE_DIM_ALLDIR" - Name: "dim.alldir" + Type: "SUN_DIRECT" + Name: "sun.direct" + Attribute { + Format: FLOAT3 + Tag: "aux[0]" + FriendlyTag: "Sheet (playback FPS, frames per row, frames total)" + Value: ( 30.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[1]" + FriendlyTag: "Sheet frame (width, height)" + Value: ( 0.0 0.0 ) + } } Flavor { - Type: "RETROREFLECTIVE_PIKO_ALLDIR" - Name: "piko.alldir" + Type: "SUN_LUCENT" + Name: "sun.lucent" + Attribute { + Format: FLOAT3 + Tag: "aux[0]" + FriendlyTag: "Sheet (playback FPS, frames per row, frames total)" + Value: ( 30.0 1.0 1.0 ) + } + Attribute { + Format: FLOAT2 + Tag: "aux[1]" + FriendlyTag: "Sheet frame (width, height)" + Value: ( 0.0 0.0 ) + } } Flavor { Type: "TEXGEN0" Name: "tg0" Attribute { - Format: FLOAT2 + Format: FLOAT4 Tag: "aux[0]" - FriendlyTag: "TexGen0 Scale (X,Z)" - Value: ( 10.0 10.0 ) + FriendlyTag: "TexGen0 (ScaleX, ScaleZ, Rotation, LodSwitch)" + Value: ( 10.0 10.0 0.0 0.0 ) } } Flavor { Type: "TEXGEN1" Name: "tg1" Attribute { - Format: FLOAT2 + Format: FLOAT4 Tag: "aux[1]" - FriendlyTag: "TexGen1 Scale (X,Z)" - Value: ( 8.0 8.0 ) + FriendlyTag: "TexGen1 (ScaleX, ScaleZ, Rotation, Unk)" + Value: ( 8.0 8.0 0.0 0.0 ) } } +Flavor { + Type: "TOSTREAM" + Name: "tostream" +} +Flavor { + Type: "TOWATER" + Name: "towater" +} +Flavor { + Type: "WATER_STREAM" + Name: "stream" +} diff --git a/addon/io_scs_tools/supported_effects.bin b/addon/io_scs_tools/supported_effects.bin index 2f3338b8..0bf92a2c 100644 Binary files a/addon/io_scs_tools/supported_effects.bin and b/addon/io_scs_tools/supported_effects.bin differ diff --git a/addon/io_scs_tools/ui/material.py b/addon/io_scs_tools/ui/material.py index ba14bee5..a1b17187 100644 --- a/addon/io_scs_tools/ui/material.py +++ b/addon/io_scs_tools/ui/material.py @@ -175,59 +175,42 @@ def draw_flavors(layout, mat): column = layout.column(align=True) column.alignment = "LEFT" - # draw switching operator for each flavor (if there is more variants draw operator for each variant) - enabled_flavors = {} # store enabled flavors, for later analyzing which rows should be disabled - """:type: dict[int, io_scs_tools.internals.shader_presets.ui_shader_preset_item.FlavorVariant]""" - flavor_rows = [] # store row UI layout of flavor operators, for later analyzing which rows should be disabled + # 1. Collect currently active flavor variants + active_flavors = {} for i, flavor in enumerate(preset.flavors): + for variant in flavor.variants: + if ("." + variant.suffix + ".") in effect_flavor_part or effect_flavor_part.endswith("." + variant.suffix): + active_flavors[i] = variant.suffix + # 2. Draw switching operator for each flavor (if there is more variants draw operator for each variant) + for i, flavor in enumerate(preset.flavors): + row = column.row(align=True) - flavor_rows.append(row) for flavor_variant in flavor.variants: - is_in_middle = "." + flavor_variant.suffix + "." in effect_flavor_part - is_on_end = effect_flavor_part.endswith("." + flavor_variant.suffix) - flavor_enabled = is_in_middle or is_on_end - - icon = _shared.get_on_off_icon(flavor_enabled) - props = row.operator("material.scs_tools_switch_flavor", text=flavor_variant.suffix, icon=icon, depress=flavor_enabled) + # is that flavor active? + is_active = ("." + flavor_variant.suffix + ".") in effect_flavor_part or effect_flavor_part.endswith("." + flavor_variant.suffix) + + # simulate turning on the flavor + simulated = [] + for j, f in enumerate(preset.flavors): + if i == j: + simulated.append(flavor_variant.suffix) + else: + suffix = active_flavors.get(j) + if suffix: + simulated.append(suffix) + new_flavor_str = "." + ".".join(simulated) if simulated else "" + + is_compatible = _shader_presets.has_section(preset.name, new_flavor_str) + + sub = row.row(align=True) + sub.enabled = is_compatible or is_active + icon = _shared.get_on_off_icon(is_active) + props = sub.operator("material.scs_tools_switch_flavor", text=flavor_variant.suffix, icon=icon, depress=is_active) props.flavor_name = flavor_variant.suffix - props.flavor_enabled = flavor_enabled - - if flavor_enabled: - enabled_flavors[i] = flavor_variant - - # now as we drawn the flavors and we know which ones are enabled, - # search the ones that are not compatible with currently enabled flavors and disable them in UI! - for i, flavor in enumerate(preset.flavors): - - # enabled flavors have to stay enabled so skip them - if i in enabled_flavors: - continue - - for flavor_variant in flavor.variants: - - # 1. construct proposed new flavor string: - # combine strings of enabled flavors and current flavor variant - new_flavor_str = "" - curr_flavor_added = False - for enabled_i in enabled_flavors.keys(): - - if i < enabled_i and not curr_flavor_added: - new_flavor_str += "." + flavor_variant.suffix - curr_flavor_added = True - - new_flavor_str += "." + enabled_flavors[enabled_i].suffix - - if not curr_flavor_added: - new_flavor_str += "." + flavor_variant.suffix - - # 2. check if proposed new flavor combination exists in cache: - # if not then row on current flavor index has to be disabled - if not _shader_presets.has_section(preset.name, new_flavor_str): - flavor_rows[i].enabled = False - break + props.flavor_enabled = is_active @staticmethod def draw_parameters(layout, mat, scs_inventories, split_perc, is_imported_shader=False): @@ -244,7 +227,9 @@ def draw_parameters(layout, mat, scs_inventories, split_perc, is_imported_shader active_vcolors = bpy.context.active_object.data.color_attributes is_valid_vcolor = _MESH_consts.default_vcol in active_vcolors is_valid_vcolor_a = _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix in active_vcolors - if not is_valid_vcolor or not is_valid_vcolor_a: + is_valid_vfactor = _MESH_consts.default_vfactor in active_vcolors + is_valid_vfactor_shader = ("piko.alldir") in mat.scs_props.mat_effect_name + if not is_valid_vcolor or not is_valid_vcolor_a or (not is_valid_vfactor and is_valid_vfactor_shader): vcol_missing_box = layout.box() @@ -260,6 +245,9 @@ def draw_parameters(layout, mat, scs_inventories, split_perc, is_imported_shader if not is_valid_vcolor_a: info_msg += "-> '" + _MESH_consts.default_vcol + _MESH_consts.vcol_a_suffix + "'\n" + if not is_valid_vfactor and is_valid_vfactor_shader: + info_msg += "-> '" + _MESH_consts.default_vfactor + "'\n" + info_msg += "You can use 'Add Vertex Colors To (Active/All)' button to add needed layers or add layers manually." _shared.draw_warning_operator(col, "Vertex Colors Missing", info_msg, text="More Info", icon='INFO') @@ -297,6 +285,7 @@ def draw_parameters(layout, mat, scs_inventories, split_perc, is_imported_shader is_aliasable = ('textures' in shader_data and ( len(shader_data["textures"]) == 1 or + (len(shader_data["textures"]) == 2 and "tsnmap" in mat.scs_props.mat_effect_name) or (len(shader_data["textures"]) == 2 and "dif.spec.weight.mult2" in mat.scs_props.mat_effect_name) )) @@ -309,7 +298,7 @@ def draw_parameters(layout, mat, scs_inventories, split_perc, is_imported_shader "-> '/material/road'\n" "-> '/material/terrain'\n" "-> '/material/custom'\n" - "Additional requirement for aliasing is also single texture material or\n" + "Additional requirement for aliasing is also single texture material (excluding nmap) or\n" "exceptionally multi texture material of 'dif.spec.weight.mult2' family.\n\n" "Currently aliasing can not be done because:") @@ -498,7 +487,7 @@ def draw_attribute_item(layout, mat, split_perc, attribute): elif tag == 'env_factor': value_layout.prop(mat.scs_props, 'shader_attribute_env_factor', text="") elif tag == 'fresnel': - value_layout.column().prop(mat.scs_props, 'shader_attribute_fresnel', text="") + value_layout.prop(mat.scs_props, 'shader_attribute_fresnel', text="") elif tag == 'tint': value_layout.prop(mat.scs_props, 'shader_attribute_tint', text="") elif tag == 'tint_opacity': @@ -587,6 +576,8 @@ def draw_texture_item(layout, mat, split_perc, texture, read_only): tag_id_string = tag_id[1] texture_type = tag_id_string[8:] shader_texture_id = str('shader_' + tag_id_string) + frendly_tag = texture.get('FriendlyTag', None) + texture_label = frendly_tag if frendly_tag else str(texture_type.title()) if hide_state == 'True': return @@ -594,7 +585,7 @@ def draw_texture_item(layout, mat, split_perc, texture, read_only): texture_box = layout.box().column() # create column for compact display with alignment header_split = texture_box.row(align=True) - header_split.label(text=texture_type.title(), icon='TEXTURE') + header_split.label(text=texture_label, icon='TEXTURE') if hasattr(mat.scs_props, shader_texture_id): @@ -772,6 +763,104 @@ def draw(self, context): self.draw_texture_item(col, mat, _UI_SPLIT_PERC, self.textures_data[texture_key], self.is_imported_shader) +class SCS_TOOLS_PT_MaterialMappings(_MaterialPanelBlDefs, Panel): + """Draws additional mappings of current material in sub-panel.""" + bl_parent_id = SCS_TOOLS_PT_Material.__name__ + bl_label = "Material Mappings" + + @classmethod + def poll(cls, context): + if not _MaterialPanelBlDefs.poll(context): + return False + + mat = context.active_object.active_material + shader_data = mat.get("scs_shader_attributes", {}) + + if len(shader_data) == 0: + return False + + if 'mappings' not in shader_data: + return False + + if len(shader_data["mappings"]) == 0: + return False + + if context.active_object.type != 'MESH': + return False + + cls.mappings_data = shader_data['mappings'] + + return True + + @staticmethod + def draw_mapping_item(layout, mat, split_perc, mapping): + """Draws mapping box with it's properties. + + :param layout: layout to draw attribute to + :type layout: bpy.types.UILayout + :param mat: material from which data should be displayed + :type mat: bpy.types.Material + :param split_perc: split percentage for attribute name/value + :type split_perc: float + :param mapping: mapping data + :type mapping: dict + """ + + tag = mapping.get('Tag', None) + shader_mapping_id = str('shader_mapping_' + tag) + frendly_tag = mapping.get('FriendlyTag', None) + mapping_label = frendly_tag if frendly_tag else str(tag) + + #lprint("D ----- Shader: %s", (shader_mapping_id,)) + + mapping_box = layout.box().column() # create column for compact display with alignment + mapping_row = mapping_box.row(align=True) + item_space = mapping_row.split(factor=split_perc, align=True) + tag_layout = item_space.row() + tag_layout.label(text=mapping_label, icon='UV') + + if hasattr(mat.scs_props, shader_mapping_id): + # UV LAYERS FOR MAPPING + uv_mappings = getattr(mat.scs_props, shader_mapping_id, []) + + if len(uv_mappings) > 0: + + layout_box_col = item_space.column(align=True) + + for mapping in uv_mappings: + + item_space_row = layout_box_col.row(align=True) + + if mapping.value and mapping.value != "" and mapping.value in bpy.context.active_object.data.uv_layers: + icon = 'GROUP_UVS' + else: + icon = 'ERROR' + + item_space_row.prop_search( + data=mapping, + property="value", + search_data=bpy.context.active_object.data, + search_property='uv_layers', + text="", + icon=icon, + ) + + else: + layout_box_col = item_space.column(align=True) + mapping_row.label(text="Unsupported Shader Mapping Type!", icon='ERROR') + + def draw(self, context): + mat = context.active_object.active_material + + # DESYNCED LOOK! + if self.draw_desynced_look(self.layout, context): + return + + col = self.layout.column(align=True) + for mapping_key in sorted(self.mappings_data.keys()): + self.draw_mapping_item(col, mat, _UI_SPLIT_PERC, self.mappings_data[mapping_key]) + + class SCS_TOOLS_PT_LooksOnMaterial(_shared.HeaderIconPanel, _MaterialPanelBlDefs, Panel): """Draws SCS Looks panel on object tab.""" @@ -795,6 +884,7 @@ def draw(self, context): classes = ( SCS_TOOLS_PT_Material, SCS_TOOLS_PT_MaterialAttributes, + SCS_TOOLS_PT_MaterialMappings, SCS_TOOLS_PT_MaterialTextures, SCS_TOOLS_PT_LooksOnMaterial, SCS_TOOLS_UL_MaterialCustomMappingSlot, diff --git a/addon/io_scs_tools/ui/object.py b/addon/io_scs_tools/ui/object.py index 4294a552..f0bb0d05 100644 --- a/addon/io_scs_tools/ui/object.py +++ b/addon/io_scs_tools/ui/object.py @@ -30,6 +30,7 @@ from io_scs_tools.utils import get_scs_inventories as _get_scs_inventories from io_scs_tools.ui import shared as _shared +_UI_SPLIT_PERC = 0.4 class _ObjectPanelBlDefs: bl_space_type = "PROPERTIES" @@ -72,9 +73,26 @@ def draw_icon_part_tools(layout, index): def draw_item(self, context, layout, data, item, icon, active_data, active_property, index): if self.layout_type in {'DEFAULT', 'COMPACT'}: if item: - line = layout.split(factor=0.6, align=False) - line.prop(item, "name", text="", emboss=False, icon_value=icon) - tools = line.row(align=True) + active_object = context.active_object + is_not_root = getattr(active_object.scs_props, 'empty_object_type', None) != 'SCS_Root' + row = layout.row(align=True) + if is_not_root: + current_part_name = getattr(active_object.scs_props, 'scs_part', None) + is_assigned = (item.name == current_part_name) + icon_col = row.column() + + if is_assigned: + icon_col.label(icon='RESTRICT_INSTANCED_OFF') + else: + op = icon_col.operator('object.scs_tools_assign_part', text="", icon='RESTRICT_INSTANCED_ON', emboss=False) + op.part_index = index + + spacer_col = row.column() + spacer_col.scale_x = 0.15 + spacer_col.label(text="") + + row.prop(item, "name", text="", emboss=False, icon_value=icon) + tools = row.row(align=True) tools.alignment = 'RIGHT' self.draw_icon_part_tools(tools, index) else: @@ -282,20 +300,38 @@ def draw_model_locator(layout, obj): layout.prop(obj.scs_props, 'locator_model_hookup_lamp_height') @staticmethod - def draw_collision_locator(layout, obj): + def draw_collision_locator(layout, obj, split_perc): layout.use_property_split = True layout.use_property_decorate = False layout.prop(obj.scs_props, 'locator_collider_type') - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + # Temporary turning off to create correct split for checkbox labels + layout.use_property_split = False + + split = layout.split(factor=split_perc) + col_label = split.column() + col_label.alignment = 'RIGHT' + col_label.label(text="Visibility") + + col_checks = split.column() + check_col_wire = col_checks.row() + check_col_wire.prop(obj.scs_props, 'locator_collider_wires', text='Draw Wireframes') + check_col_face = col_checks.row() + check_col_face.prop(obj.scs_props, 'locator_collider_faces', text='Draw Faces') - col = flow.column() - col.prop(obj.scs_props, 'locator_collider_wires', text='Draw Wireframes') - col = flow.column() - col.prop(obj.scs_props, 'locator_collider_faces', text='Draw Faces') if obj.scs_props.locator_collider_type != 'Convex': - layout.prop(obj.scs_props, 'locator_collider_centered') + + split = layout.split(factor=split_perc) + col_label = split.column() + col_label.alignment = 'RIGHT' + col_label.label(text="Properties") + + col_checks = split.column() + check_convex = col_checks.row() + check_convex.prop(obj.scs_props, 'locator_collider_centered') + + layout.use_property_split = True layout.prop(obj.scs_props, 'locator_collider_mass') @@ -366,8 +402,24 @@ def draw_prefab_spawn_point(layout, obj): layout.use_property_split = True layout.use_property_decorate = False + # spawn type layout.prop(obj.scs_props, 'locator_prefab_spawn_type') + # check if Spawn Type = Custom (9) + if obj.scs_props.locator_prefab_spawn_type == str(_PL_consts.PSP.CUSTOM): + + # depot type (load/unload) + layout.prop(obj.scs_props, 'locator_prefab_custom_depot_type', expand=True, toggle=True) + + # parking difficulty + layout.prop(obj.scs_props, 'locator_prefab_custom_parking_difficulty') + + # lenght + layout.prop(obj.scs_props, 'locator_prefab_custom_lenght') + + # rule (trailer type) + layout.prop(obj.scs_props, 'locator_prefab_custom_rule') + @staticmethod def draw_prefab_semaphore(layout, obj): layout.use_property_split = True @@ -405,18 +457,24 @@ def draw_prefab_semaphore(layout, obj): loc_set.prop(obj.scs_props, 'locator_prefab_tsem_cyc_delay') @staticmethod - def draw_prefab_navigation_point(layout, context, obj): - layout.use_property_split = True + def draw_prefab_navigation_point(layout, context, obj, split_perc): + layout.use_property_split = False layout.use_property_decorate = False - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + split = layout.split(factor=split_perc) + col_label = split.column() + col_label.alignment = 'RIGHT' + col_label.label(text="Properties") - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_np_low_probab') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_np_add_priority') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_np_limit_displace') + col_checks = split.column() + check_low_prob = col_checks.row() + check_low_prob.prop(obj.scs_props, 'locator_prefab_np_low_probab') + check_add_prio = col_checks.row() + check_add_prio.prop(obj.scs_props, 'locator_prefab_np_add_priority') + check_limit_disp = col_checks.row() + check_limit_disp.prop(obj.scs_props, 'locator_prefab_np_limit_displace') + + layout.use_property_split = True # allowed vehicles layout.prop(obj.scs_props, 'locator_prefab_np_allowed_veh') @@ -468,25 +526,31 @@ def draw_prefab_navigation_point(layout, context, obj): loc_set.operator('object.scs_tools_connect_prefab_locators', text="Connect / Disconnect Navigation Points", icon='LINKED') @staticmethod - def draw_prefab_map_point(layout, context, obj): + def draw_prefab_map_point(layout, context, obj, split_perc): # box_row_box = layout - layout.use_property_split = True + layout.use_property_split = False layout.use_property_decorate = False is_polygon = int(obj.scs_props.locator_prefab_mp_road_size) == _PL_consts.MPVF.ROAD_SIZE_MANUAL - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + split = layout.split(factor=split_perc) + col_label = split.column() + col_label.alignment = 'RIGHT' + col_label.label(text="Properties") + + col_checks = split.column() + check_road_ov = col_checks.row() + check_road_ov.prop(obj.scs_props, 'locator_prefab_mp_road_over') + check_no_out = col_checks.row() + check_no_out.prop(obj.scs_props, 'locator_prefab_mp_no_outline') + check_no_arr = col_checks.row() + check_no_arr.enabled = not is_polygon + check_no_arr.prop(obj.scs_props, 'locator_prefab_mp_no_arrow') + check_pr_exit = col_checks.row() + check_pr_exit.enabled = not is_polygon + check_pr_exit.prop(obj.scs_props, 'locator_prefab_mp_prefab_exit') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_mp_road_over') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_mp_no_outline') - col = flow.column() - col.enabled = not is_polygon - col.prop(obj.scs_props, 'locator_prefab_mp_no_arrow') - col = flow.column() - col.enabled = not is_polygon - col.prop(obj.scs_props, 'locator_prefab_mp_prefab_exit') + layout.use_property_split = True layout.prop(obj.scs_props, 'locator_prefab_mp_road_size') row = _shared.create_row(layout, use_split=True, enabled=not is_polygon) @@ -519,7 +583,7 @@ def draw_prefab_map_point(layout, context, obj): loc_set.operator('object.scs_tools_connect_prefab_locators', text="Connect / Disconnect Map Points", icon='LINKED') @staticmethod - def draw_prefab_trigger_point(layout, context, obj): + def draw_prefab_trigger_point(layout, context, obj, split_perc): layout.use_property_split = True layout.use_property_decorate = False @@ -531,16 +595,25 @@ def draw_prefab_trigger_point(layout, context, obj): layout.prop(obj.scs_props, 'locator_prefab_tp_range') layout.prop(obj.scs_props, 'locator_prefab_tp_reset_delay') - flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + # Temporary turning off to create correct split for checkbox labels + layout.use_property_split = False + + split = layout.split(factor=split_perc) + col_label = split.column() + col_label.alignment = 'RIGHT' + col_label.label(text="Properties") - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_tp_sphere_trigger') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_tp_partial_activ') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_tp_onetime_activ') - col = flow.column() - col.prop(obj.scs_props, 'locator_prefab_tp_manual_activ') + col_checks = split.column() + check_sph_tr = col_checks.row() + check_sph_tr.prop(obj.scs_props, 'locator_prefab_tp_sphere_trigger') + check_part_act = col_checks.row() + check_part_act.prop(obj.scs_props, 'locator_prefab_tp_partial_activ') + check_one_tim_act = col_checks.row() + check_one_tim_act.prop(obj.scs_props, 'locator_prefab_tp_onetime_activ') + check_man_act = col_checks.row() + check_man_act.prop(obj.scs_props, 'locator_prefab_tp_manual_activ') + + layout.use_property_split = True loc_set = layout.row() if len(context.selected_objects) == 2: @@ -581,7 +654,7 @@ def draw(self, context): # COLLISION LOCATORS elif obj.scs_props.locator_type == 'Collision': - self.draw_collision_locator(layout, obj) + self.draw_collision_locator(layout, obj, _UI_SPLIT_PERC) # PREFAB LOCATORS elif obj.scs_props.locator_type == 'Prefab': @@ -597,11 +670,11 @@ def draw(self, context): elif obj.scs_props.locator_prefab_type == 'Traffic Semaphore': self.draw_prefab_semaphore(layout, obj) elif obj.scs_props.locator_prefab_type == 'Navigation Point': - self.draw_prefab_navigation_point(layout, context, obj) + self.draw_prefab_navigation_point(layout, context, obj, _UI_SPLIT_PERC) elif obj.scs_props.locator_prefab_type == 'Map Point': - self.draw_prefab_map_point(layout, context, obj) + self.draw_prefab_map_point(layout, context, obj, _UI_SPLIT_PERC) elif obj.scs_props.locator_prefab_type == 'Trigger Point': - self.draw_prefab_trigger_point(layout, context, obj) + self.draw_prefab_trigger_point(layout, context, obj, _UI_SPLIT_PERC) class SCS_TOOLS_PT_PreviewModel(_ObjectPanelBlDefs, Panel): @@ -1136,7 +1209,7 @@ def draw(self, context): sub = layout_row.row(align=True) sub.scale_x = 10 if not screen.is_animation_playing: - if scene.sync_mode == 'AUDIO_SYNC' and context.user_preferences.system.audio_device == 'JACK': + if scene.sync_mode == 'AUDIO_SYNC' and context.preferences.system.audio_device == 'JACK': sub.operator("screen.animation_play", text="", icon='PLAY') else: sub.operator("screen.animation_play", text="", icon='PLAY_REVERSE').reverse = True diff --git a/addon/io_scs_tools/ui/shared.py b/addon/io_scs_tools/ui/shared.py index 26d95188..5c6d2ea7 100644 --- a/addon/io_scs_tools/ui/shared.py +++ b/addon/io_scs_tools/ui/shared.py @@ -194,6 +194,8 @@ def draw_common_settings(layout, log_level_only=False, without_box=False): if not log_level_only: sub_layout.prop(_get_scs_globals(), 'config_storage_place') + sub_layout.prop(_get_scs_globals(), 'activate_new_parts') + sub_layout.prop(_get_scs_globals(), 'activate_new_variant_parts') def draw_warning_operator(layout, title, message, text="", icon='ERROR'): diff --git a/addon/io_scs_tools/ui/tool_shelf.py b/addon/io_scs_tools/ui/tool_shelf.py index 754fa02b..a605c481 100644 --- a/addon/io_scs_tools/ui/tool_shelf.py +++ b/addon/io_scs_tools/ui/tool_shelf.py @@ -22,6 +22,7 @@ from bpy.types import Panel from io_scs_tools.consts import Icons as _ICONS_consts from io_scs_tools.consts import LampTools as _LT_consts +from io_scs_tools.consts import InteriorWindowTools as _IWT_consts from io_scs_tools.consts import Operators as _OP_consts from io_scs_tools.consts import VertexColorTools as _VCT_consts from io_scs_tools.internals.icons import get_icon @@ -464,16 +465,19 @@ def draw(self, context): props.vehicle_side = _LT_consts.VehicleSides.FrontRight.name props.aux_color = props.traffic_light_color = "" body_row = body_col.row(align=True) + props = body_row.operator("mesh.scs_tools_set_lampmask_uv", text="Middle Left") + props.vehicle_side = _LT_consts.VehicleSides.MiddleLeft.name + props.aux_color = props.traffic_light_color = "" + props = body_row.operator("mesh.scs_tools_set_lampmask_uv", text="Middle Right") + props.vehicle_side = _LT_consts.VehicleSides.MiddleRight.name + props.aux_color = props.traffic_light_color = "" + body_row = body_col.row(align=True) props = body_row.operator("mesh.scs_tools_set_lampmask_uv", text="Rear Left") props.vehicle_side = _LT_consts.VehicleSides.RearLeft.name props.aux_color = props.traffic_light_color = "" props = body_row.operator("mesh.scs_tools_set_lampmask_uv", text="Rear Right") props.vehicle_side = _LT_consts.VehicleSides.RearRight.name props.aux_color = props.traffic_light_color = "" - body_row = body_col.row(align=True) - props = body_row.operator("mesh.scs_tools_set_lampmask_uv", text="Middle") - props.vehicle_side = _LT_consts.VehicleSides.Middle.name - props.aux_color = props.traffic_light_color = "" body_col = layout.column(align=True) body_row = body_col.row(align=True) @@ -502,6 +506,35 @@ def draw(self, context): props.traffic_light_color = _LT_consts.TrafficLightTypes.Green.name props.vehicle_side = props.aux_color = "" +class SCS_TOOLS_PT_InteriorWindowTool(_ToolShelfBlDefs, Panel): + """ + Creates Interior Window Tool panel for SCS Tools tab. + """ + bl_label = "Interior Window Tool" + + @classmethod + def poll(cls, context): + return context.mode in {'EDIT_MESH', 'OBJECT'} + + def draw(self, context): + if not self.poll(context): + self.layout.label(text="Not in 'Edit/Object Mode'!", icon="INFO") + return + + layout = self.layout + + body_col = layout.column(align=True) + body_row = body_col.row(align=True) + body_row.label(text="Glass Reflection", icon='MOD_LATTICE') + + body_row = body_col.row(align=True) + props = body_row.operator("mesh.scs_tools_set_glassreflection_uv", text="Enable") + props.glass_state = _IWT_consts.GlassReflection.Enable.name + props = body_row.operator("mesh.scs_tools_set_glassreflection_uv", text="Disable") + props.glass_state = _IWT_consts.GlassReflection.Disable.name + + body_col = layout.column(align=True) + body_col.operator("mesh.scs_tools_fix_vertex_normals", text="Fix Vertex Normals", icon='NORMALS_VERTEX_FACE') class SCS_TOOLS_PT_VColoring(_ToolShelfBlDefs, Panel): bl_label = "VColoring" @@ -590,6 +623,7 @@ def draw(self, context): SCS_TOOLS_PT_DisplayMethods, SCS_TOOLS_PT_LampSwitcher, SCS_TOOLS_PT_LampTool, + SCS_TOOLS_PT_InteriorWindowTool, SCS_TOOLS_PT_VColoring, SCS_TOOLS_PT_VertexColorStatsTool, SCS_TOOLS_PT_VertexColorWrapTool, @@ -607,6 +641,7 @@ def register(): SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - Display Methods", SCS_TOOLS_PT_DisplayMethods.__name__) SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - Lamp Switcher", SCS_TOOLS_PT_LampSwitcher.__name__) SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - Lamp Tool", SCS_TOOLS_PT_LampTool.__name__) + SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - Interior Window Tool", SCS_TOOLS_PT_InteriorWindowTool.__name__) SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - VColoring", SCS_TOOLS_PT_VColoring.__name__) SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - VColor Stats", SCS_TOOLS_PT_VertexColorStatsTool.__name__) SCS_TOOLS_MT_MainMenu.append_sidebar_entry("Sidebar - VColor Wrap", SCS_TOOLS_PT_VertexColorWrapTool.__name__) diff --git a/addon/io_scs_tools/ui/workspace.py b/addon/io_scs_tools/ui/workspace.py index e9f3c2a5..a199a420 100644 --- a/addon/io_scs_tools/ui/workspace.py +++ b/addon/io_scs_tools/ui/workspace.py @@ -222,6 +222,36 @@ def draw(self, context): layout.prop(scs_globals, 'locator_coll_face_color') +class SCS_TOOLS_PT_SpawnPointDisplay(_WorkspacePanelBlDefs, Panel): + """Draw spawn point display panel.""" + + bl_parent_id = SCS_TOOLS_PT_LocatorsDisplay.__name__ + bl_label = "Spawn Points" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.get_layout() + scs_globals = _get_scs_globals() + + # scs tools main panel if config is being updated + layout.enabled = not scs_globals.config_update_lock + + layout.use_property_split = True + layout.use_property_decorate = False + layout.enabled = scs_globals.display_locators and not scs_globals.config_update_lock + + layout.prop(scs_globals, 'show_trailer_type') + layout.prop(scs_globals, 'trailer_load_easy_color') + layout.prop(scs_globals, 'trailer_load_medium_color') + layout.prop(scs_globals, 'trailer_load_hard_color') + layout.prop(scs_globals, 'trailer_unload_easy_color') + layout.prop(scs_globals, 'trailer_unload_medium_color') + layout.prop(scs_globals, 'trailer_unload_hard_color') + + layout.prop(scs_globals, 'owned_trailer_color') + layout.prop(scs_globals, 'service_station_color') + + class SCS_TOOLS_PT_ConnectionsDisplay(_WorkspacePanelBlDefs, Panel): """Draw connections display panel.""" @@ -277,6 +307,7 @@ def draw(self, context): SCS_TOOLS_PT_PathSettings, SCS_TOOLS_PT_DisplaySettings, SCS_TOOLS_PT_LocatorsDisplay, + SCS_TOOLS_PT_SpawnPointDisplay, SCS_TOOLS_PT_ConnectionsDisplay, SCS_TOOLS_PT_OtherSetttings, ) diff --git a/addon/io_scs_tools/unused/pim.py b/addon/io_scs_tools/unused/pim.py index 79f04fbb..af57a03a 100644 --- a/addon/io_scs_tools/unused/pim.py +++ b/addon/io_scs_tools/unused/pim.py @@ -52,7 +52,7 @@ def _fill_header_section(file_name, output_type): if source_filename == "": source_filename = "Unsaved" header_section.props.append(("SourceFilename", source_filename)) - author = bpy.context.user_preferences.system.author + author = bpy.context.preferences.system.author if author: header_section.props.append(("Author", str(author))) return header_section diff --git a/addon/io_scs_tools/utils/info.py b/addon/io_scs_tools/utils/info.py index ed36fc83..b6bdb456 100644 --- a/addon/io_scs_tools/utils/info.py +++ b/addon/io_scs_tools/utils/info.py @@ -128,3 +128,32 @@ def cmp_ver_str(version_str, version_str2): # otherwise we directly assume that first is greater return 1 + +def cmp_ver_str_unofficial(version_str, version_str2): + """Compares two version string of format "X.X.X..." where X is number. + + :param version_str: version string to check (should be in format: "X.Y.ZZZZ.[U]" where X and Y are version numbers, ZZZZ hash, and U is optional unofficial update version) + :type version_str: str + :param version_str2: version string to check (should be in format: "U" where U is unofficial update version) + :type version_str2: str + :return: -1 if first is smaller; 0 if equal; 1 if first is greater; + :rtype: int + """ + + version_str = version_str.split(".") + version_str2 = version_str2.split(".") + + # Fix for users migrating from official BT (without unofficial update version at BT version) + while len(version_str) <= 3: + version_str.append('0') + + # First version smaller than second + if int(version_str[3]) < int(version_str2[0]): + return -1 + + # Equal versions + if int(version_str[3]) == int(version_str2[0]): + return 0 + + # Otherwise we directly assume that first is greater + return 1 \ No newline at end of file diff --git a/addon/io_scs_tools/utils/material.py b/addon/io_scs_tools/utils/material.py index 34be39ce..9698b606 100644 --- a/addon/io_scs_tools/utils/material.py +++ b/addon/io_scs_tools/utils/material.py @@ -308,11 +308,11 @@ def get_reflection_image(texture_path, report_invalid=False): camera.type = "PANO" camera.lens = 5 camera.sensor_width = 32 - camera.cycles.panorama_type = "EQUIRECTANGULAR" - camera.cycles.latitude_min = -pi * 0.5 - camera.cycles.latitude_max = pi * 0.5 - camera.cycles.longitude_min = pi - camera.cycles.longitude_max = -pi + camera.panorama_type = "EQUIRECTANGULAR" + camera.latitude_min = -pi * 0.5 + camera.latitude_max = pi * 0.5 + camera.longitude_min = pi + camera.longitude_max = -pi cam_obj = bpy.data.objects.new(camera.name, camera) cam_obj.location = (0,) * 3 @@ -392,8 +392,8 @@ def get_material_info(obj): for slot in obj.material_slots: if slot.material: - - if obj.scs_props.scs_part.startswith("coll"): + # "_col..." used in additional parts, "..._c" used in curve models + if obj.scs_props.scs_part.startswith(("coll", "_col")) or obj.scs_props.scs_part.endswith(("_c")): has_static_collision = True effect_name = slot.material.scs_props.mat_effect_name.lower() @@ -436,10 +436,13 @@ def set_shader_data_to_material(material, section, is_import=False, override_bac attributes = {} textures = {} + mappings = {} attribute_i = 0 texture_i = 0 + mapping_i = 0 used_attribute_types = {} # attribute types used by current shader used_texture_types = {} # texture types used by current shader and should be overlooked during clearing of texture slots + used_mapping_types = {} # UV mapping types used by current shader (created especially for shaders that use UV without textures like 'interior' shader) for item in section.sections: if item.type == "Attribute": @@ -475,6 +478,20 @@ def set_shader_data_to_material(material, section, is_import=False, override_bac textures[str(texture_i)] = texture_data texture_i += 1 + elif item.type == "Mapping": + uvmapping_data = {} + for prop in item.props: + # print(' prop: "%s"' % str(prop)) + uvmapping_data[prop[0]] = prop[1] + + # APPLY SECTION MAPPING VALUES + mapping_type = uvmapping_data['Tag'] + + used_mapping_types[mapping_type] = uvmapping_data + + mappings[str(mapping_i)] = uvmapping_data + mapping_i += 1 + scs_props_keys = set(material.scs_props.keys()) # if overriding back data also make sure to clear attribute values for current shader # to prevent storing of unused values from blend data block @@ -492,6 +509,11 @@ def set_shader_data_to_material(material, section, is_import=False, override_bac if used_tex_type in key[14:]: is_key_used = True + if key.startswith("shader_mapping"): + for used_map_type in used_mapping_types: + if used_map_type in key[14:]: + is_key_used = True + # delete only unused shader keys everything else should stay in the place # as those keys might be used in some other way if not is_key_used and key.startswith("shader_"): @@ -677,12 +699,72 @@ def set_shader_data_to_material(material, section, is_import=False, override_bac settings, map_type = _tobj_imp.get_settings_and_type(tobj_abs_path) created_tex_settings[tex_type] = settings + # collect old uv mappings per tex_coord values and delete them from material (they will be set back later) + old_uv_mappings = {} + for mapping_type in used_mapping_types: + + uv_mappings = getattr(material.scs_props, "shader_mapping_" + mapping_type, []) + if override_back_data: + while len(uv_mappings) > 0: + + # save only set uv mapping tex_cord:value pairs + if uv_mappings[0].value != "": + old_uv_mappings[uv_mappings[0].tex_coord] = uv_mappings[0].value + + uv_mappings.remove(0) + + # apply used mappings + created_uv_mappings = [] + for mapping_type in used_mapping_types.keys(): + + # skip unknown mapping type + if mapping_type not in ("perturbation", "vertex_pos", "unknown"): + lprint("D Trying to apply unknown mapping type to SCS material: %r", (mapping_type,)) + continue + + uvmapping_data = used_mapping_types[mapping_type] + + uv_mappings = getattr(material.scs_props, "shader_mapping_" + mapping_type) + + # if there is an info about mapping in shader use it (in case of imported material this condition will fall!) + if "TexCoord" in uvmapping_data: + + for tex_coord_i, tex_coord in enumerate(uvmapping_data['TexCoord']): + tex_coord = int(tex_coord) + + if tex_coord != -1: + mapping = uv_mappings.add() + mapping['mapping_type'] = mapping_type + mapping['tex_coord'] = tex_coord + + # apply uv mappings either from imported data or from old mappings of previous shader + if "scs_tex_aliases" in material: # scs_tex_aliases are present only on import + + # if mesh is corrupted then tex aliases won't be filled in properly in material from PIM importer, + # so report error and skip creation of texture mapping for current tex_coord. + if str(tex_coord) not in material["scs_tex_aliases"]: + lprint("E Material %r is missing mapping coordinate aliases, some UV mappings in Material will remain empty!", + (material.name,)) + continue + + mapping['value'] = material["scs_tex_aliases"][str(tex_coord)] + created_uv_mappings.append((mapping_type, mapping.value, tex_coord)) + + elif tex_coord in old_uv_mappings: + + mapping['value'] = old_uv_mappings[tex_coord] + created_uv_mappings.append((mapping_type, mapping.value, tex_coord)) + + else: + lprint("D Trying to apply negative tex_coord in SCS Material Mappings: %r", (mapping_type,)) + # override shader data for identifying used attributes and textures in UI if override_back_data: shader_data = {'effect': preset_effect, 'attributes': attributes, - 'textures': textures} + 'textures': textures, + 'mappings' : mappings} material["scs_shader_attributes"] = shader_data # setup nodes for 3D view visualization @@ -696,6 +778,14 @@ def set_shader_data_to_material(material, section, is_import=False, override_bac # data[2] = tex coord value _shader.set_uv(material, mapping_data[0], mapping_data[1], mapping_data[2]) + for mapping_data in created_uv_mappings: + + # data[0] = mapping type; + # data[1] = uv mapping value; + # data[2] = tex coord value + + _shader.set_mapping(material, mapping_data[0], mapping_data[1], mapping_data[2]) + def reload_tobj_settings(material, tex_type): """Relaods TOBJ settings on given texture type of material. @@ -806,7 +896,7 @@ def set_texture_settings_to_node(tex_node, settings): # linear colorspace if settings[0] == "1": - image.colorspace_settings.name = "Linear" + image.colorspace_settings.name = "Linear Rec.709" else: image.colorspace_settings.name = "sRGB" @@ -815,7 +905,7 @@ def set_texture_settings_to_node(tex_node, settings): if image.filepath[-4:] in (".tga", ".dds"): image.colorspace_settings.name = "Non-Color" elif image.filepath[-4:] == ".png" and image.is_float: - image.colorspace_settings.name = "Linear" + image.colorspace_settings.name = "Linear Rec.709" def has_valid_color_management(scene): diff --git a/addon/io_scs_tools/utils/mesh.py b/addon/io_scs_tools/utils/mesh.py index 26c45372..d24fd2b2 100644 --- a/addon/io_scs_tools/utils/mesh.py +++ b/addon/io_scs_tools/utils/mesh.py @@ -27,7 +27,7 @@ from io_scs_tools.utils import convert as _convert -def make_points_to_weld_list(mesh_vertices, mesh_normals, mesh_rgb, mesh_rgba, equal_decimals_count): +def make_points_to_weld_list(mesh_vertices, mesh_normals, mesh_rgb, mesh_rgba, mesh_factor, equal_decimals_count): """Makes a map of duplicated vertices indices into it's original counter part.""" # take first present vertex color data @@ -38,6 +38,11 @@ def make_points_to_weld_list(mesh_vertices, mesh_normals, mesh_rgb, mesh_rgba, e else: mesh_final_rgba = {} + if mesh_factor: + mesh_final_factor = mesh_factor + else: + mesh_final_factor = {} + posnorm_dict_tmp = {} perc = 10 ** equal_decimals_count # represent precision for duplicates for val_i, val in enumerate(mesh_vertices): @@ -50,6 +55,11 @@ def make_points_to_weld_list(mesh_vertices, mesh_normals, mesh_rgb, mesh_rgba, e for col_channel in mesh_final_rgba[vc_layer_name][val_i]: key += str(int(col_channel * perc)) + # also include factor vertex colors in key if present + for vfc_layer_name in mesh_final_factor: + for col_channel in mesh_final_factor[vfc_layer_name][val_i]: + key += str(int(col_channel * perc)) + if key not in posnorm_dict_tmp: posnorm_dict_tmp[key] = [val_i] else: @@ -233,6 +243,34 @@ def bm_make_vc_layer(pim_version, bm, vc_layer_name, vc_layer_data): vcol_a = (alpha / 2,) * 3 + (1.0,) loop[color_a_lay] = vcol_a +def bm_make_vfc_layer(pim_version, bm, vfc_layer_name, vfc_layer_data): + """Add Vertex Color Layer for Factor to the BMesh object. + + :param pim_version: PIM version of the File from which data have been read + :type pim_version: int + :param bm: BMesh data to add Vertex Color Layer to + :type bm: bmesh.types.BMesh + :param vfc_layer_name: Name for the layer + :type vfc_layer_name: str + :param vfc_layer_data: Vertex Color Layer data + :type vfc_layer_data: list + """ + # only 5 and 7 versions are supported currently + assert (pim_version == 5 or pim_version == 7) + + color_lay = bm.loops.layers.color.new(vfc_layer_name) + + for face_i, face in enumerate(bm.faces): + f_v = [x.index for x in face.verts] + for loop_i, loop in enumerate(face.loops): + if pim_version == 5: + vfcol = vfc_layer_data[f_v[loop_i]] + else: + vfcol = vfc_layer_data[face_i][loop_i] + + # Technically, vfcol[3] is repeated vfcol[2] value, and probably is not even used in the game, but just in case use vfcol[3] + vfcol = (vfcol[0] / 255, vfcol[1] / 255, vfcol[2] / 255, vfcol[3] / 255 ) + loop[color_lay] = vfcol def bm_delete_loose(mesh): """Deletes loose vertices in the mesh. @@ -311,23 +349,21 @@ def get_mesh_for_normals(mesh): new_mesh = mesh.copy() # if user is not using auto smooth, then apply it now just for the porpuse of proper normals split calculation. - if not new_mesh.use_auto_smooth: - new_mesh.use_auto_smooth = True - new_mesh.auto_smooth_angle = 3.14 - - new_mesh.calc_normals_split() + # NOTE: auto_smooth property has been replaced by a modifier node group asset in Blender 4.1, so now users should use modifier yourself? + # if not new_mesh.use_auto_smooth: + # new_mesh.use_auto_smooth = True + # new_mesh.auto_smooth_angle = 3.14 return new_mesh def cleanup_mesh(mesh): - """Frees normals split and removes mesh if possible. + """Removes mesh if possible. :param mesh: mesh to be cleaned :type mesh: bpy.types.Mesh """ - mesh.free_normals_split() if mesh.users == 0: bpy.data.meshes.remove(mesh, do_unlink=True)