From d6827b4a9c57ac4ecf2af5f6b0aeb6fe5416f32a Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Thu, 11 Aug 2022 00:13:26 -0700 Subject: [PATCH 001/103] Upversion for dev only --- MCprep_addon/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/__init__.py b/MCprep_addon/__init__.py index 8ff29c12..b8683067 100755 --- a/MCprep_addon/__init__.py +++ b/MCprep_addon/__init__.py @@ -41,7 +41,7 @@ bl_info = { "name": "MCprep", "category": "Object", - "version": (3, 4, 1), + "version": (3, 4, 1, 1), "blender": (2, 80, 0), "location": "3D window toolshelf > MCprep tab", "description": "Minecraft workflow addon for rendering and animation", From c07e339ffbc20e1ee6b71600f9273427f6bac37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Thu, 25 Aug 2022 16:40:33 +0700 Subject: [PATCH 002/103] add effects to user preferences and advance option --- MCprep_addon/mcprep_ui.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 10be9e7f..fd01eb96 100755 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -559,6 +559,21 @@ def draw(self, context): p = col.operator("mcprep.openfolder", text="Open skin folder") p.folder = self.skin_path + row = layout.row() + row.scale_y = 0.7 + row.label(text="Effects") + box = layout.box() + split = util.layout_split(box, factor=factor_width) + col = split.column() + col.label(text="Effect folder") + col = split.column() + col.prop(self, "effects_path", text="") + split = util.layout_split(box, factor=factor_width) + col = split.column() + col = split.column() + p = col.operator("mcprep.openfolder", text="Open effects folder") + p.folder = self.effects_path + # misc settings row = layout.row() row.prop(self, "verbose") @@ -1525,6 +1540,26 @@ def effects_spawner(self, context): ops.location = util.get_cuser_location(context) ops.frame = context.scene.frame_current + col = layout.column() + if not scn_props.show_settings_effect: + col.prop( + scn_props, "show_settings_effect", + text="Advanced", icon="TRIA_RIGHT") + else: + col.prop( + scn_props, "show_settings_effect", + text="Advanced", icon="TRIA_DOWN") + box = col.box() + b_row = box.row() + b_col = b_row.column(align=False) + b_col.label(text="Effects") + subrow = b_col.row(align=True) + subrow.prop(context.scene, "mcprep_effects_path", text="") + b_row = box.row() + b_col = b_row.column(align=True) + b_col.operator("mcprep.reload_effects", + text="Reload effects") + class MCPREP_PT_spawn(bpy.types.Panel): """MCprep panel for mob spawning""" @@ -1776,6 +1811,10 @@ class McprepProps(bpy.types.PropertyGroup): name="show spawner settings", description="Show extra MCprep panel settings", default=False) + show_settings_effect = bpy.props.BoolProperty( + name="show effect settings", + description="Show extra MCprep panel settings", + default=False) # Rig settings spawn_rig_category = bpy.props.EnumProperty( From 5f9d7ccd679c555b06bd01f806ff5da69e00763f Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Tue, 27 Sep 2022 22:09:30 -0700 Subject: [PATCH 003/103] Effects path reset operator and advanced UI --- MCprep_addon/mcprep_ui.py | 35 +++++++++++++++++++++++++++++++++ MCprep_addon/spawner/effects.py | 15 ++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 10be9e7f..05f9ecdf 100755 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -504,6 +504,12 @@ def draw(self, context): row = box.row() row.label(text="Entity file not found", icon="ERROR") + col = split.column() + col.prop(self, "effects_path", text="") + if not os.path.isdir(bpy.path.abspath(self.effects_path)): + row = box.row() + row.label(text="Effects folder not found", icon="ERROR") + row = layout.row() row.scale_y = 0.7 row.label(text="Texture / Resource packs") @@ -1525,6 +1531,35 @@ def effects_spawner(self, context): ops.location = util.get_cuser_location(context) ops.frame = context.scene.frame_current + if not scn_props.show_settings_spawner: + col.prop( + scn_props, "show_settings_spawner", + text="Advanced", icon="TRIA_RIGHT") + else: + col.prop( + scn_props, "show_settings_spawner", + text="Advanced", icon="TRIA_DOWN") + box = col.box() + b_row = box.row() + b_col = b_row.column(align=False) + b_col.label(text="Effects folder") + subrow = b_col.row(align=True) + subrow.prop(context.scene, "mcprep_effects_path", text="") + subrow.operator("mcprep.effects_path_reset", icon=LOAD_FACTORY, text="") + + base = bpy.path.abspath(context.scene.mcprep_effects_path) + if not os.path.isdir(base): + b_col.label(text="Effects folder not found", icon="ERROR") + elif not os.path.isdir(os.path.join(base, "collection")): + b_col.label(text="Effects/collection folder not found", icon="ERROR") + elif not os.path.isdir(os.path.join(base, "geonodes")): + b_col.label(text="Effects/geonodes folder not found", icon="ERROR") + elif not os.path.isdir(os.path.join(base, "particle")): + b_col.label(text="Effects/particle folder not found", icon="ERROR") + b_row = box.row() + b_col = b_row.column(align=True) + b_col.operator("mcprep.reload_effects") + class MCPREP_PT_spawn(bpy.types.Panel): """MCprep panel for mob spawning""" diff --git a/MCprep_addon/spawner/effects.py b/MCprep_addon/spawner/effects.py index d41f1772..a3c33294 100644 --- a/MCprep_addon/spawner/effects.py +++ b/MCprep_addon/spawner/effects.py @@ -967,6 +967,20 @@ def load_image_sequence_effects(context): # ----------------------------------------------------------------------------- +class MCPREP_OT_effects_path_reset(bpy.types.Operator): + """Reset the effects path to the default specified in the addon preferences panel""" + bl_idname = "mcprep.effects_path_reset" + bl_label = "Reset effects path" + bl_options = {'REGISTER', 'UNDO'} + + @tracking.report_error + def execute(self, context): + addon_prefs = util.get_user_preferences(context) + context.scene.mcprep_effects_path = addon_prefs.effects_path + update_effects_list(context) + return {'FINISHED'} + + class MCPREP_OT_reload_effects(bpy.types.Operator): """Reload effects spawner, use after changes to/new effects files""" bl_idname = "mcprep.reload_effects" @@ -1164,6 +1178,7 @@ def execute(self, context): classes = ( + MCPREP_OT_effects_path_reset, MCPREP_OT_reload_effects, MCPREP_OT_global_effect, MCPREP_OT_instant_effect, From 52fe35044c54a643635c548b324bd9f24d6d61d2 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Tue, 27 Sep 2022 22:16:38 -0700 Subject: [PATCH 004/103] Fix 'not a library' issue, prompt users to reset spawner paths Resolved longstanding issue where users try to spawn a mob or other entity, when the source folder has been lost. The adodn now proactively raises a prompt to suggest resetting spawner paths to addon defaults --- MCprep_addon/spawner/mobs.py | 20 ++++++++----- MCprep_addon/spawner/spawn_util.py | 46 ++++++++++++++++++++++++++++-- MCprep_addon/util.py | 33 +++++++++++++++------ 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/MCprep_addon/spawner/mobs.py b/MCprep_addon/spawner/mobs.py index 2de37c72..0d7219ab 100644 --- a/MCprep_addon/spawner/mobs.py +++ b/MCprep_addon/spawner/mobs.py @@ -347,10 +347,13 @@ def getCategories(self, context): path = bpy.path.abspath(context.scene.mcprep_mob_path) ret = [] ret.append(("all", "No Category", "Uncategorized mob")) - ret += [ - (f, f.title(), "{} mob".format(f.title())) - for f in os.listdir(path) - if os.path.isdir(os.path.join(path, f)) and not f.startswith(".")] + try: + ret += [ + (f, f.title(), "{} mob".format(f.title())) + for f in os.listdir(path) + if os.path.isdir(os.path.join(path, f)) and not f.startswith(".")] + except FileNotFoundError: + pass # mobs folder not found, so just pass. ret.append(("no_category", "No Category", "Uncategorized mob")) # last entry return ret @@ -668,9 +671,12 @@ def spawn_rigs_categories(self, context): categories = conf.rig_categories if not conf.rig_categories: it = context.scene.mcprep_mob_path - categories = [ - f for f in os.listdir(it) if os.path.isdir(os.path.join(it, f))] - conf.rig_categories = categories + try: + categories = [ + f for f in os.listdir(it) if os.path.isdir(os.path.join(it, f))] + conf.rig_categories = categories + except FileNotFoundError: + pass # Directory has changed or is not found. for item in categories: ui_name = item + " mobs" items.append(( diff --git a/MCprep_addon/spawner/spawn_util.py b/MCprep_addon/spawner/spawn_util.py index dd9e88f7..d448dbf6 100755 --- a/MCprep_addon/spawner/spawn_util.py +++ b/MCprep_addon/spawner/spawn_util.py @@ -339,15 +339,22 @@ def load_linked(self, context, path, name): path = bpy.path.abspath(path) act = None if hasattr(bpy.data, "groups"): - util.bAppendLink(path + '/Group', name, True) + res = util.bAppendLink(path + '/Group', name, True) act = context.object # assumption of object after linking, 2.7 only elif hasattr(bpy.data, "collections"): - util.bAppendLink(path + '/Collection', name, True) + res = util.bAppendLink(path + '/Collection', name, True) if len(context.selected_objects) > 0: act = context.selected_objects[0] # better for 2.8 else: print("Error: Should have had at least one object selected.") + if res is False: + # Most likely scenario, path was wrong and raised "not a library". + # end and automatically reload assets. + self.report({'WARNING'}, "Failed to load asset file") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return + # util.bAppendLink(os.path.join(path, g_or_c), name, True) # proxy any and all armatures # here should do all that jazz with hacking by copying files, checking @@ -466,10 +473,16 @@ def load_append(self, context, path, name): conf.log(os.path.join(path, subpath) + ', ' + name) pregroups = list(util.collections()) - util.bAppendLink(os.path.join(path, subpath), name, False) + res = util.bAppendLink(os.path.join(path, subpath), name, False) postgroups = list(util.collections()) new_groups = list(set(postgroups) - set(pregroups)) + if res is False: + # Most likely scenario, path was wrong and raised "not a library". + # end and automatically reload assets. + self.report({'WARNING'}, "Failed to load asset file") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return if not new_groups and name in util.collections(): # this is more likely to fail but serves as a fallback conf.log("Mob spawn: Had to go to fallback group name grab") @@ -632,6 +645,32 @@ def execute(self, context): return {'FINISHED'} +class MCPREP_OT_prompt_reset_spawners(bpy.types.Operator): + """Reset the all spawner paths to the default in addon preferences""" + bl_idname = "mcprep.prompt_reset_spawners" + bl_label = "Reset spawn path" + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + col = self.layout.column(align=True) + col.scale_y = 0.8 + col.label(text="A spawner directory/file is missing.") + col.label(text="Reset all spawner paths to default?") + + @tracking.report_error + def execute(self, context): + bpy.ops.mcprep.spawn_path_reset() + bpy.ops.mcprep.reset_texture_path() + bpy.ops.mcprep.effects_path_reset() + bpy.ops.mcprep.entity_path_reset() + bpy.ops.mcprep.meshswap_path_reset() + + return {'FINISHED'} + + # ----------------------------------------------------------------------------- # UI list related # ----------------------------------------------------------------------------- @@ -864,6 +903,7 @@ class ListEffectsAssets(bpy.types.PropertyGroup): MCPREP_UL_effects, MCPREP_OT_reload_spawners, MCPREP_OT_spawn_path_reset, + MCPREP_OT_prompt_reset_spawners, ) diff --git a/MCprep_addon/util.py b/MCprep_addon/util.py index 296f6071..586d5718 100755 --- a/MCprep_addon/util.py +++ b/MCprep_addon/util.py @@ -122,6 +122,8 @@ def bAppendLink(directory, name, toLink, active_layer=True): directory: xyz.blend/Type, where Type is: Collection, Group, Material... name: asset name toLink: bool + + Returns: true if successful, false if not. """ conf.log("Appending " + directory + " : " + name, vv_only=True) @@ -133,21 +135,36 @@ def bAppendLink(directory, name, toLink, active_layer=True): if "link_append" in dir(bpy.ops.wm): # OLD method of importing, e.g. in blender 2.70 conf.log("Using old method of append/link, 2.72 <=", vv_only=True) - bpy.ops.wm.link_append(directory=directory, filename=name, link=toLink) + try: + bpy.ops.wm.link_append(directory=directory, filename=name, link=toLink) + return True + except RuntimeError as e: + print("bAppendLink", e) + return False elif "link" in dir(bpy.ops.wm) and "append" in dir(bpy.ops.wm): conf.log("Using post-2.72 method of append/link", vv_only=True) if toLink: bpy.ops.wm.link(directory=directory, filename=name) elif bv28(): - bpy.ops.wm.append( - directory=directory, - filename=name) + try: + bpy.ops.wm.append( + directory=directory, + filename=name) + return True + except RuntimeError as e: + print("bAppendLink", e) + return False else: conf.log("{} {} {}".format(directory, name, active_layer)) - bpy.ops.wm.append( - directory=directory, - filename=name, - active_layer=active_layer) + try: + bpy.ops.wm.append( + directory=directory, + filename=name, + active_layer=active_layer) + return True + except RuntimeError as e: + print("bAppendLink", e) + return False def obj_copy(base, context=None, vertex_groups=True, modifiers=True): From b741d2bf8f9863d03aa89f1f90506e62dabcbf9c Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Tue, 27 Sep 2022 22:58:48 -0700 Subject: [PATCH 005/103] Better identify when to prompt resetting resoruce pack paths Similar to prior commit, this change ensures that the popup is made whenever trying to spawn an effect/item/model where we know up front the filepath doesn't exist. Often happens if you are downloading a file save on another machine with absolute paths. This change prompts the user to take the action to reset paths to defaults so that they have an immediate way to resolve the issue, but can click off if they want to manually fix the paths in advanced settings. --- MCprep_addon/spawner/effects.py | 46 ++++++++++++++++++++++++------ MCprep_addon/spawner/item.py | 6 +++- MCprep_addon/spawner/mcmodel.py | 3 +- MCprep_addon/spawner/meshswap.py | 8 +++--- MCprep_addon/spawner/spawn_util.py | 2 +- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/MCprep_addon/spawner/effects.py b/MCprep_addon/spawner/effects.py index a3c33294..a8110986 100644 --- a/MCprep_addon/spawner/effects.py +++ b/MCprep_addon/spawner/effects.py @@ -1033,6 +1033,12 @@ def poll(cls, context): def execute(self, context): mcprep_props = context.scene.mcprep_props effect = mcprep_props.effects_list[int(self.effect_id)] + + if not os.path.isfile(bpy.path.abspath(effect.filepath)): + self.report({'WARNING'}, "Target effects file not found") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return {'CANCELLED'} + if effect.effect_type == GEO_AREA: add_geonode_area_effect(context, effect) self.report( @@ -1089,12 +1095,17 @@ def effects_enum(self, context): effect_id = bpy.props.EnumProperty(items=effects_enum, name="Effect") location = bpy.props.FloatVectorProperty(default=(0, 0, 0), name="Location") frame = bpy.props.IntProperty( - default=0, name="Frame", description="Start frame for animation") + default=0, + name="Frame", + description="Start frame for animation") speed = bpy.props.FloatProperty( - default=1.0, min=0.1, name="Speed", + default=1.0, + min=0.1, + name="Speed", description="Make the effect run faster (skip frames) or slower (hold frames)") show_image = bpy.props.BoolProperty( - default=True, name="Show image preview", + default=False, + name="Show image preview", description="Show a middle animation frame as a viewport preview") skipUsage = bpy.props.BoolProperty(default=False, options={'HIDDEN'}) @@ -1107,16 +1118,27 @@ def poll(cls, context): def execute(self, context): mcprep_props = context.scene.mcprep_props effect = mcprep_props.effects_list[int(self.effect_id)] + if effect.effect_type == "collection": - add_collection_effect( - context, effect, self.location, self.frame) - return {'FINISHED'} + if not os.path.isfile(bpy.path.abspath(effect.filepath)): + self.report({'WARNING'}, "Target effects file not found") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return {'CANCELLED'} + add_collection_effect(context, effect, self.location, self.frame) + elif effect.effect_type == "img_seq": + base_path = os.path.dirname(effect.filepath) + if not os.path.isdir(bpy.path.abspath(base_path)): + self.report({'WARNING'}, "Target effects file not found: base_path") + print(base_path) + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return {'CANCELLED'} + inst = add_image_sequence_effect( context, effect, self.location, self.frame, self.speed) - if not self.show_image: inst.data = None + self.track_param = effect.name return {'FINISHED'} @@ -1149,8 +1171,14 @@ def execute(self, context): name = os.path.basename(path) name = os.path.splitext(name)[0] - add_particle_planes_effect( - context, self.filepath, self.location, self.frame) + try: + add_particle_planes_effect( + context, self.filepath, self.location, self.frame) + except FileNotFoundError as e: + print("Particle effect file error:", e) + self.report({'WARNING'}, "File not found") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return {'CANCELLED'} self.track_param = name return {'FINISHED'} diff --git a/MCprep_addon/spawner/item.py b/MCprep_addon/spawner/item.py index 66cbadbb..97a1b176 100644 --- a/MCprep_addon/spawner/item.py +++ b/MCprep_addon/spawner/item.py @@ -375,7 +375,11 @@ def spawn_item_execution(self, context): # apply additional settings # generate materials via prep, without re-loading image datablock - if status and not obj: + if status == "File not found" and not obj: + self.report({'WARNING'}, status) + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') + return {'CANCELLED'} + elif status and not obj: self.report({'ERROR'}, status) return {'CANCELLED'} elif status: diff --git a/MCprep_addon/spawner/mcmodel.py b/MCprep_addon/spawner/mcmodel.py index d91301e4..705aab9b 100644 --- a/MCprep_addon/spawner/mcmodel.py +++ b/MCprep_addon/spawner/mcmodel.py @@ -470,7 +470,8 @@ class MCPREP_OT_spawn_minecraft_model(bpy.types.Operator, ModelSpawnBase): def execute(self, context): name = os.path.basename(os.path.splitext(self.filepath)[0]) if not self.filepath or not os.path.isfile(self.filepath): - self.report({"ERROR"}, "Filepath not found") + self.report({"WARNING"}, "Filepath not found") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') return {'CANCELLED'} if not self.filepath.lower().endswith(".json"): self.report( diff --git a/MCprep_addon/spawner/meshswap.py b/MCprep_addon/spawner/meshswap.py index b2738662..589bd664 100755 --- a/MCprep_addon/spawner/meshswap.py +++ b/MCprep_addon/spawner/meshswap.py @@ -551,16 +551,16 @@ def execute(self, context): direc = context.scene.meshswap_path if not direc.lower().endswith('.blend'): - self.report({'ERROR'}, "Meshswap file must be a .blend!") + self.report({'WARNING'}, "Meshswap file must be a .blend!") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') return {'CANCELLED'} if not os.path.isfile(direc): direc = bpy.path.abspath(direc) - # DOES work! but less streamlined - # bpy.ops.object.dialogue('INVOKE_DEFAULT') if not os.path.isfile(direc): # better, actual "error" popup. - self.report({'ERROR'}, "Meshswap blend file not found!") + self.report({'WARNING'}, "Meshswap blend file not found!") + bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') return {'CANCELLED'} # Assign vars used across operator diff --git a/MCprep_addon/spawner/spawn_util.py b/MCprep_addon/spawner/spawn_util.py index d448dbf6..90651d41 100755 --- a/MCprep_addon/spawner/spawn_util.py +++ b/MCprep_addon/spawner/spawn_util.py @@ -648,7 +648,7 @@ def execute(self, context): class MCPREP_OT_prompt_reset_spawners(bpy.types.Operator): """Reset the all spawner paths to the default in addon preferences""" bl_idname = "mcprep.prompt_reset_spawners" - bl_label = "Reset spawn path" + bl_label = "Reset spawner & texturepath paths" bl_options = {'REGISTER', 'UNDO'} def invoke(self, context, event): From c356fb88ef504a8dc2083cf29df57abe8b483186 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sat, 15 Oct 2022 21:18:33 -0400 Subject: [PATCH 006/103] Revert dev value change --- MCprep_addon/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/conf.py b/MCprep_addon/conf.py index dd9083f4..7a40de58 100644 --- a/MCprep_addon/conf.py +++ b/MCprep_addon/conf.py @@ -39,7 +39,7 @@ def init(): # Used to print out extra information, set false with distribution # ----------------------------------------------- global dev - dev = False + dev = True global v v = True # $VERBOSE, UI setting From 543ed500c98a972317e9520cc13d029fb6d9672e Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Sun, 16 Oct 2022 14:04:46 -0500 Subject: [PATCH 007/103] Added some info on internal refactoring --- CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3757c3aa..b3cebc3b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,6 +26,19 @@ This is largely possible for a few reasons: 1. Abstracting API changes vs directly implementing changes. Instead of swapping "group" for "collection" in the change to blender 2.8, we create if/else statements and wrapper functions that fetch the attribute that exists based on the version of blender. Want more info about this? See [the article here](https://theduckcow.com/2019/update-addons-both-blender-28-and-27-support/). 1. No python annotations. This syntax wasn't supported in old versions of python that came with blender (namely, in Blender 2.7) and so we don't use annotations in this repository. Some workarounds are in place to avoid excessive printouts as a result. +## Internal Rewrites +MCprep has a separate branch for internal rewrites based on the dev branch. Sometimes, internal tools are deprecated, and requires features to be changed to reflect those deprecations. + +Developers should not worry about this. If a features uses depracated features, it will be fixed in the next internal rewrite. + +It may be tempting to try and use the `internal-rewrites` branch to write new features. Don't, as pull requests for `internal-rewrites` will only be accepted if they deal with rewriting parts of MCprep itself. Pull requests for new features that only use `internal-rewrites` for the sake of avoiding the use of deprecated features will not be accepted. + +Also, when something is deprecated, it will still remain in MCprep for one full version and be removed by the next release. For instance, if during the development of MCprep X a feature is deprecated, then that feature will only be removed officially starting with the development of MCprep Y. Thus, any new features in MCprep X that use a deprecated feature will be fixed in the `internal-rewrites` branch for the development cycle of MCprep Y. + +There will also be a pull request for `internal-rewrites` for each devlopment cycle of MCprep. This pull request will contain all newly deprecated and removed features, as well as their replacements. This exists as a heads up to developers so that they know what to expect. + +New features go into the `dev` branch, rewriting of old features to account for deprecations go into the `internal-rewrites` branch. When in doubt, simply ask. + ## Compiling and running tests As above, a critical component of maintaining support and ensuring the wide number of MCprep features are stable, is running automated tests. From 6811fa339829a822354d86d6e82f28d1dcbdd914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Wed, 7 Dec 2022 12:10:58 +0700 Subject: [PATCH 008/103] use `create_node` for generate node --- MCprep_addon/materials/generate.py | 100 +++++++++++++++++------------ 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 09c472d5..940ee70b 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -958,6 +958,15 @@ def set_saturation_material(mat): sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) +def create_node(tree_nodes, node_type): + """Function to create node""" + # For MixRGB in 3.4 become Legacy + if node_type == 'ShaderNodeMixRGB' and bpy.app.version >= (3, 4, 0): + node = tree_nodes.new('ShaderNodeMix') + node.data_type = 'RGBA' + else: + node = tree_nodes.new(node_type) + return node # ----------------------------------------------------------------------------- # Generating node groups @@ -1041,14 +1050,14 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the neccecary nodes - nodeTexDiff = nodes.new("ShaderNodeTexImage") - nodeTexNorm = nodes.new("ShaderNodeTexImage") - nodeTexSpec = nodes.new("ShaderNodeTexImage") + nodeTexDiff = create_node(nodes,"ShaderNodeTexImage") + nodeTexNorm = create_node(nodes,"ShaderNodeTexImage") + nodeTexSpec = create_node(nodes,"ShaderNodeTexImage") - nodeSpecInv = nodes.new("ShaderNodeInvert") - nodeSaturateMix = nodes.new("ShaderNodeMixRGB") - nodeNormal = nodes.new("ShaderNodeNormalMap") - nodeNormalInv = nodes.new("ShaderNodeRGBCurve") + nodeSpecInv = create_node(nodes,"ShaderNodeInvert") + nodeSaturateMix = create_node(nodes,"ShaderNodeMixRGB") + nodeNormal = create_node(nodes,"ShaderNodeNormalMap") + nodeNormalInv = create_node(nodes,"ShaderNodeRGBCurve") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Texture" @@ -1162,14 +1171,14 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the neccecary nodes - nodeTexDiff = nodes.new("ShaderNodeTexImage") - nodeTexNorm = nodes.new("ShaderNodeTexImage") - nodeTexSpec = nodes.new("ShaderNodeTexImage") - nodeSpecInv = nodes.new("ShaderNodeInvert") - nodeSeperate = nodes.new("ShaderNodeSeparateRGB") - nodeSaturateMix = nodes.new("ShaderNodeMixRGB") - nodeNormal = nodes.new("ShaderNodeNormalMap") - nodeNormalInv = nodes.new("ShaderNodeRGBCurve") + nodeTexDiff = create_node(nodes,"ShaderNodeTexImage") + nodeTexNorm = create_node(nodes,"ShaderNodeTexImage") + nodeTexSpec = create_node(nodes,"ShaderNodeTexImage") + nodeSpecInv = create_node(nodes,"ShaderNodeInvert") + nodeSeperate = create_node(nodes,"ShaderNodeSeparateRGB") + nodeSaturateMix = create_node(nodes,"ShaderNodeMixRGB") + nodeNormal = create_node(nodes,"ShaderNodeNormalMap") + nodeNormalInv = create_node(nodes,"ShaderNodeRGBCurve") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Texture" @@ -1313,8 +1322,8 @@ def matgen_cycles_simple( nodeSaturateMix = nodes.new("ShaderNodeMixRGB") nodeSaturateMix.name = "Add Color" nodeSaturateMix.label = "Add Color" - principled = nodes.new("ShaderNodeBsdfPrincipled") - node_out = nodes.new("ShaderNodeOutputMaterial") + principled = create_node(nodes,"ShaderNodeBsdfPrincipled") + node_out = create_node(nodes,"ShaderNodeOutputMaterial") # set location nodeSaturateMix.location = (300, 0) @@ -1571,30 +1580,36 @@ def matgen_cycles_original( links = mat.node_tree.links nodes.clear() - nodeMixDiff = nodes.new("ShaderNodeMixShader") + # Shader nodeDiff = nodes.new("ShaderNodeBsdfDiffuse") + nodeGlossDiff = nodes.new("ShaderNodeBsdfGlossy") + nodeGlossMetallic = nodes.new("ShaderNodeBsdfGlossy") + nodeTrans = nodes.new("ShaderNodeBsdfTransparent") + nodeEmit = nodes.new("ShaderNodeEmission") + nodeEmitCam = nodes.new("ShaderNodeEmission") + # Mix Shader + nodeMixDiff = nodes.new("ShaderNodeMixShader") + nodeMixMetallic = nodes.new("ShaderNodeMixShader") + nodeMixTrans = nodes.new("ShaderNodeMixShader") + nodeMixEmit = nodes.new("ShaderNodeMixShader") + nodeMixCam = nodes.new("ShaderNodeMixShader") + # Mix nodeMixRGBDiff = nodes.new("ShaderNodeMixRGB") nodeMixRGB = nodes.new("ShaderNodeMixRGB") + nodeMixRGBMetallic = nodes.new("ShaderNodeMixRGB") + # Input nodeFresnel = nodes.new("ShaderNodeFresnel") - nodeMathPower = nodes.new("ShaderNodeMath") + nodeFresnelMetallic = nodes.new("ShaderNodeFresnel") + nodeLightPath = nodes.new("ShaderNodeLightPath") nodeGeometry = nodes.new("ShaderNodeNewGeometry") - nodeBump = nodes.new("ShaderNodeBump") + # Math + nodeMathPower = nodes.new("ShaderNodeMath") nodeMathPowerDiff = nodes.new("ShaderNodeMath") nodeMathMultiplyDiff = nodes.new("ShaderNodeMath") - nodeGlossDiff = nodes.new("ShaderNodeBsdfGlossy") - nodeFresnelMetallic = nodes.new("ShaderNodeFresnel") nodeMathMetallic = nodes.new("ShaderNodeMath") - nodeMixRGBMetallic = nodes.new("ShaderNodeMixRGB") - nodeGlossMetallic = nodes.new("ShaderNodeBsdfGlossy") - nodeMixMetallic = nodes.new("ShaderNodeMixShader") + # Etc + nodeBump = nodes.new("ShaderNodeBump") nodeFalloff = nodes.new("ShaderNodeLightFalloff") - nodeLightPath = nodes.new("ShaderNodeLightPath") - nodeEmit = nodes.new("ShaderNodeEmission") - nodeEmitCam = nodes.new("ShaderNodeEmission") - nodeMixCam = nodes.new("ShaderNodeMixShader") - nodeMixEmit = nodes.new("ShaderNodeMixShader") - nodeTrans = nodes.new("ShaderNodeBsdfTransparent") - nodeMixTrans = nodes.new("ShaderNodeMixShader") nodeOut = nodes.new("ShaderNodeOutputMaterial") # set location @@ -1792,16 +1807,16 @@ def matgen_special_water(mat, passes): links = mat.node_tree.links nodes.clear() - nodeTexDiff = nodes.new('ShaderNodeTexImage') - nodeTexNorm = nodes.new('ShaderNodeTexImage') - nodeNormal = nodes.new('ShaderNodeNormalMap') - nodeNormalInv = nodes.new('ShaderNodeRGBCurve') - nodeBrightContrast = nodes.new('ShaderNodeBrightContrast') - nodeSaturateMix = nodes.new('ShaderNodeMixRGB') - nodeGlass = nodes.new('ShaderNodeBsdfGlass') - nodeTrans = nodes.new('ShaderNodeBsdfTransparent') - nodeMixTrans = nodes.new('ShaderNodeMixShader') - nodeOut = nodes.new('ShaderNodeOutputMaterial') + nodeTexDiff = create_node(nodes,'ShaderNodeTexImage') + nodeTexNorm = create_node(nodes,'ShaderNodeTexImage') + nodeNormal = create_node(nodes,'ShaderNodeNormalMap') + nodeNormalInv = create_node(nodes,'ShaderNodeRGBCurve') + nodeBrightContrast = create_node(nodes,'ShaderNodeBrightContrast') + nodeSaturateMix = create_node(nodes,'ShaderNodeMixRGB') + nodeGlass = create_node(nodes,'ShaderNodeBsdfGlass') + nodeTrans = create_node(nodes,'ShaderNodeBsdfTransparent') + nodeMixTrans = create_node(nodes,'ShaderNodeMixShader') + nodeOut = create_node(nodes,'ShaderNodeOutputMaterial') # set location nodeTexDiff.location = (-180, 140) @@ -1949,6 +1964,7 @@ def matgen_special_glass(mat, passes): nodeNormalInv = nodes.new('ShaderNodeRGBCurve') nodeGlass = nodes.new('ShaderNodeBsdfGlass') nodeBrightContrast = nodes.new('ShaderNodeBrightContrast') + nodeFrame = nodes.new("NodeFrame") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Tex" From 63b05b48b8a3502d23f1939b92d11720c2c58ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Wed, 7 Dec 2022 13:18:30 +0700 Subject: [PATCH 009/103] update all to create_node, add sockets --- MCprep_addon/materials/generate.py | 140 +++++++++++++++-------------- 1 file changed, 73 insertions(+), 67 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 940ee70b..459917d0 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -960,13 +960,19 @@ def set_saturation_material(mat): def create_node(tree_nodes, node_type): """Function to create node""" - # For MixRGB in 3.4 become Legacy + # For any node MixRGB in 3.4 become Legacy + sockets= { + "inputs": inputs, + "outputs": outputs + } if node_type == 'ShaderNodeMixRGB' and bpy.app.version >= (3, 4, 0): node = tree_nodes.new('ShaderNodeMix') node.data_type = 'RGBA' + inputs = [5,6] + outputs = [2] else: node = tree_nodes.new(node_type) - return node + return node,sockets # ----------------------------------------------------------------------------- # Generating node groups @@ -1050,14 +1056,14 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the neccecary nodes - nodeTexDiff = create_node(nodes,"ShaderNodeTexImage") - nodeTexNorm = create_node(nodes,"ShaderNodeTexImage") - nodeTexSpec = create_node(nodes,"ShaderNodeTexImage") + nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") + nodeTexNorm = create_node(nodes, "ShaderNodeTexImage") + nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") - nodeSpecInv = create_node(nodes,"ShaderNodeInvert") - nodeSaturateMix = create_node(nodes,"ShaderNodeMixRGB") - nodeNormal = create_node(nodes,"ShaderNodeNormalMap") - nodeNormalInv = create_node(nodes,"ShaderNodeRGBCurve") + nodeSpecInv = create_node(nodes, "ShaderNodeInvert") + nodeSaturateMix, sockets = create_node(nodes, "ShaderNodeMixRGB") + nodeNormal = create_node(nodes, "ShaderNodeNormalMap") + nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Texture" @@ -1171,14 +1177,14 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the neccecary nodes - nodeTexDiff = create_node(nodes,"ShaderNodeTexImage") - nodeTexNorm = create_node(nodes,"ShaderNodeTexImage") - nodeTexSpec = create_node(nodes,"ShaderNodeTexImage") - nodeSpecInv = create_node(nodes,"ShaderNodeInvert") - nodeSeperate = create_node(nodes,"ShaderNodeSeparateRGB") - nodeSaturateMix = create_node(nodes,"ShaderNodeMixRGB") - nodeNormal = create_node(nodes,"ShaderNodeNormalMap") - nodeNormalInv = create_node(nodes,"ShaderNodeRGBCurve") + nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") + nodeTexNorm = create_node(nodes, "ShaderNodeTexImage") + nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") + nodeSpecInv = create_node(nodes, "ShaderNodeInvert") + nodeSeperate = create_node(nodes, "ShaderNodeSeparateRGB") + nodeSaturateMix, sockets = create_node(nodes, "ShaderNodeMixRGB") + nodeNormal = create_node(nodes, "ShaderNodeNormalMap") + nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Texture" @@ -1318,12 +1324,12 @@ def matgen_cycles_simple( links = mat.node_tree.links nodes.clear() - nodeTexDiff = nodes.new("ShaderNodeTexImage") - nodeSaturateMix = nodes.new("ShaderNodeMixRGB") + nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") + nodeSaturateMix, sockets = create_node(nodes, "ShaderNodeMixRGB") nodeSaturateMix.name = "Add Color" nodeSaturateMix.label = "Add Color" - principled = create_node(nodes,"ShaderNodeBsdfPrincipled") - node_out = create_node(nodes,"ShaderNodeOutputMaterial") + principled = create_node(nodes, "ShaderNodeBsdfPrincipled") + node_out = create_node(nodes, "ShaderNodeOutputMaterial") # set location nodeSaturateMix.location = (300, 0) @@ -1435,16 +1441,16 @@ def matgen_cycles_principled( links = mat.node_tree.links nodes.clear() - principled = nodes.new("ShaderNodeBsdfPrincipled") - nodeEmit = nodes.new("ShaderNodeEmission") - nodeEmitCam = nodes.new("ShaderNodeEmission") - nodeMixCam = nodes.new("ShaderNodeMixShader") - nodeFalloff = nodes.new("ShaderNodeLightFalloff") - nodeLightPath = nodes.new("ShaderNodeLightPath") - nodeMixEmit = nodes.new("ShaderNodeMixShader") - nodeTrans = nodes.new("ShaderNodeBsdfTransparent") - nodeMixTrans = nodes.new("ShaderNodeMixShader") - nodeOut = nodes.new("ShaderNodeOutputMaterial") + principled = create_node(nodes, "ShaderNodeBsdfPrincipled") + nodeEmit = create_node(nodes, "ShaderNodeEmission") + nodeEmitCam = create_node(nodes, "ShaderNodeEmission") + nodeMixCam = create_node(nodes, "ShaderNodeMixShader") + nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff") + nodeLightPath = create_node(nodes, "ShaderNodeLightPath") + nodeMixEmit = create_node(nodes, "ShaderNodeMixShader") + nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent") + nodeMixTrans = create_node(nodes, "ShaderNodeMixShader") + nodeOut = create_node(nodes, "ShaderNodeOutputMaterial") # set location nodeEmit.location = (120, 140) @@ -1581,36 +1587,36 @@ def matgen_cycles_original( nodes.clear() # Shader - nodeDiff = nodes.new("ShaderNodeBsdfDiffuse") - nodeGlossDiff = nodes.new("ShaderNodeBsdfGlossy") - nodeGlossMetallic = nodes.new("ShaderNodeBsdfGlossy") - nodeTrans = nodes.new("ShaderNodeBsdfTransparent") - nodeEmit = nodes.new("ShaderNodeEmission") - nodeEmitCam = nodes.new("ShaderNodeEmission") + nodeDiff = create_node(nodes, "ShaderNodeBsdfDiffuse") + nodeGlossDiff = create_node(nodes, "ShaderNodeBsdfGlossy") + nodeGlossMetallic = create_node(nodes, "ShaderNodeBsdfGlossy") + nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent") + nodeEmit = create_node(nodes, "ShaderNodeEmission") + nodeEmitCam = create_node(nodes, "ShaderNodeEmission") # Mix Shader - nodeMixDiff = nodes.new("ShaderNodeMixShader") - nodeMixMetallic = nodes.new("ShaderNodeMixShader") - nodeMixTrans = nodes.new("ShaderNodeMixShader") - nodeMixEmit = nodes.new("ShaderNodeMixShader") - nodeMixCam = nodes.new("ShaderNodeMixShader") + nodeMixDiff = create_node(nodes, "ShaderNodeMixShader") + nodeMixMetallic = create_node(nodes, "ShaderNodeMixShader") + nodeMixTrans = create_node(nodes, "ShaderNodeMixShader") + nodeMixEmit = create_node(nodes, "ShaderNodeMixShader") + nodeMixCam = create_node(nodes, "ShaderNodeMixShader") # Mix - nodeMixRGBDiff = nodes.new("ShaderNodeMixRGB") - nodeMixRGB = nodes.new("ShaderNodeMixRGB") - nodeMixRGBMetallic = nodes.new("ShaderNodeMixRGB") + nodeMixRGBDiff, mixDiffSockets = create_node(nodes, "ShaderNodeMixRGB") + nodeMixRGB, mixSockets = create_node(nodes, "ShaderNodeMixRGB") + nodeMixRGBMetallic, mixMetallicSockets = create_node(nodes, "ShaderNodeMixRGB") # Input - nodeFresnel = nodes.new("ShaderNodeFresnel") - nodeFresnelMetallic = nodes.new("ShaderNodeFresnel") - nodeLightPath = nodes.new("ShaderNodeLightPath") - nodeGeometry = nodes.new("ShaderNodeNewGeometry") + nodeFresnel = create_node(nodes, "ShaderNodeFresnel") + nodeFresnelMetallic = create_node(nodes, "ShaderNodeFresnel") + nodeLightPath = create_node(nodes, "ShaderNodeLightPath") + nodeGeometry = create_node(nodes, "ShaderNodeNewGeometry") # Math - nodeMathPower = nodes.new("ShaderNodeMath") - nodeMathPowerDiff = nodes.new("ShaderNodeMath") - nodeMathMultiplyDiff = nodes.new("ShaderNodeMath") - nodeMathMetallic = nodes.new("ShaderNodeMath") + nodeMathPower = create_node(nodes, "ShaderNodeMath") + nodeMathPowerDiff = create_node(nodes, "ShaderNodeMath") + nodeMathMultiplyDiff = create_node(nodes, "ShaderNodeMath") + nodeMathMetallic = create_node(nodes, "ShaderNodeMath") # Etc - nodeBump = nodes.new("ShaderNodeBump") - nodeFalloff = nodes.new("ShaderNodeLightFalloff") - nodeOut = nodes.new("ShaderNodeOutputMaterial") + nodeBump = create_node(nodes, "ShaderNodeBump") + nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff") + nodeOut = create_node(nodes, "ShaderNodeOutputMaterial") # set location nodeMixDiff.location = (1140, 40) @@ -1812,7 +1818,7 @@ def matgen_special_water(mat, passes): nodeNormal = create_node(nodes,'ShaderNodeNormalMap') nodeNormalInv = create_node(nodes,'ShaderNodeRGBCurve') nodeBrightContrast = create_node(nodes,'ShaderNodeBrightContrast') - nodeSaturateMix = create_node(nodes,'ShaderNodeMixRGB') + nodeSaturateMix, sockets = create_node(nodes,'ShaderNodeMixRGB') nodeGlass = create_node(nodes,'ShaderNodeBsdfGlass') nodeTrans = create_node(nodes,'ShaderNodeBsdfTransparent') nodeMixTrans = create_node(nodes,'ShaderNodeMixShader') @@ -1955,16 +1961,16 @@ def matgen_special_glass(mat, passes): links = mat.node_tree.links nodes.clear() - nodeDiff = nodes.new('ShaderNodeBsdfDiffuse') - nodeMixTrans = nodes.new('ShaderNodeMixShader') - nodeOut = nodes.new('ShaderNodeOutputMaterial') - nodeTexDiff = nodes.new('ShaderNodeTexImage') - nodeTexNorm = nodes.new('ShaderNodeTexImage') - nodeNormal = nodes.new('ShaderNodeNormalMap') - nodeNormalInv = nodes.new('ShaderNodeRGBCurve') - nodeGlass = nodes.new('ShaderNodeBsdfGlass') - nodeBrightContrast = nodes.new('ShaderNodeBrightContrast') - nodeFrame = nodes.new("NodeFrame") + nodeDiff = create_node(nodes, 'ShaderNodeBsdfDiffuse') + nodeMixTrans = create_node(nodes, 'ShaderNodeMixShader') + nodeOut = create_node(nodes, 'ShaderNodeOutputMaterial') + nodeTexDiff = create_node(nodes, 'ShaderNodeTexImage') + nodeTexNorm = create_node(nodes, 'ShaderNodeTexImage') + nodeNormal = create_node(nodes, 'ShaderNodeNormalMap') + nodeNormalInv = create_node(nodes, 'ShaderNodeRGBCurve') + nodeGlass = create_node(nodes, 'ShaderNodeBsdfGlass') + nodeBrightContrast = create_node(nodes, 'ShaderNodeBrightContrast') + nodeFrame = create_node(nodes, "NodeFrame") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Tex" From 7576593ff7d103438ac3d82ce7dee66d255218eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Wed, 7 Dec 2022 13:19:51 +0700 Subject: [PATCH 010/103] remove nodeFrame --- MCprep_addon/materials/generate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 459917d0..c34a1c8a 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1970,7 +1970,6 @@ def matgen_special_glass(mat, passes): nodeNormalInv = create_node(nodes, 'ShaderNodeRGBCurve') nodeGlass = create_node(nodes, 'ShaderNodeBsdfGlass') nodeBrightContrast = create_node(nodes, 'ShaderNodeBrightContrast') - nodeFrame = create_node(nodes, "NodeFrame") # Names and labels the neccecary nodes nodeTexDiff.name = "Diffuse Tex" From 37cf0836c49bf15b7c945e4186f972656c0704aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Wed, 7 Dec 2022 13:42:09 +0700 Subject: [PATCH 011/103] Change inputs string to use index For the new Mix Node use A ,B ,Result --- MCprep_addon/materials/generate.py | 65 +++++++++++++++++++----------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index c34a1c8a..28329d58 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -960,19 +960,23 @@ def set_saturation_material(mat): def create_node(tree_nodes, node_type): """Function to create node""" - # For any node MixRGB in 3.4 become Legacy sockets= { "inputs": inputs, "outputs": outputs } - if node_type == 'ShaderNodeMixRGB' and bpy.app.version >= (3, 4, 0): - node = tree_nodes.new('ShaderNodeMix') - node.data_type = 'RGBA' - inputs = [5,6] - outputs = [2] + if node_type == 'ShaderNodeMixRGB': # For MixRGB in 3.4 become Legacy + if bpy.app.version >= (3, 4, 0): + node = tree_nodes.new('ShaderNodeMix') + node.data_type = 'RGBA' + inputs = [0,5,6] + outputs = [2] + else: + node = tree_nodes.new('ShaderNodeMixRGB') + inputs = [0,1,2] + outputs = [0] else: node = tree_nodes.new(node_type) - return node,sockets + return node, sockets # ----------------------------------------------------------------------------- # Generating node groups @@ -1083,6 +1087,8 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) + saturateMixIn = sockets["inputs"] + saturateMixOut = sockets["outputs"] # Positions the nodes nodeTexDiff.location = (-380, 140) nodeTexNorm.location = (-680, -500) @@ -1093,13 +1099,13 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeNormalInv.location = (-380, -500) # Links the nodes to the reroute nodes. - links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs["Color1"]) + links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) links.new(nodeTexNorm.outputs["Color"], nodeNormalInv.inputs["Color"]) links.new(nodeNormalInv.outputs["Color"], nodeNormal.inputs["Color"]) links.new(nodeTexSpec.outputs["Color"], nodeSpecInv.inputs["Color"]) for i in nodeInputs[0]: - links.new(nodeSaturateMix.outputs["Color"], i) + links.new(nodeSaturateMix.outputs[saturateMixOut[0]], i) for i in nodeInputs[1]: links.new(nodeTexDiff.outputs["Alpha"], i) if image_spec and use_reflections: @@ -1206,6 +1212,8 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) + saturateMixIn = sockets["inputs"] + saturateMixOut = sockets["outputs"] # Positions the nodes nodeTexDiff.location = (-380, 140) nodeTexSpec.location = (-580, -180) @@ -1217,14 +1225,14 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormalInv.location = (-380, -500) # Links the nodes to the reroute nodes. - links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs["Color1"]) + links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) links.new(nodeTexNorm.outputs["Color"], nodeNormalInv.inputs["Color"]) links.new(nodeNormalInv.outputs["Color"], nodeNormal.inputs["Color"]) links.new(nodeTexSpec.outputs["Color"], nodeSeperate.inputs["Image"]) links.new(nodeSeperate.outputs["R"], nodeSpecInv.inputs["Color"]) for i in nodeInputs[0]: - links.new(nodeSaturateMix.outputs["Color"], i) + links.new(nodeSaturateMix.outputs[saturateMixOut[0]], i) for i in nodeInputs[1]: links.new(nodeTexDiff.outputs["Alpha"], i) if image_spec and use_reflections: @@ -1350,6 +1358,7 @@ def matgen_cycles_simple( else: principled.inputs["Metallic"].default_value = 0 + saturateMixIn = sockets["inputs"] # Specular tends to cause some issues with how blocks look, so let's disable it principled.inputs["Specular"].default_value = 0 @@ -1386,7 +1395,7 @@ def matgen_cycles_simple( if hasattr(nodeTexDiff, "interpolation"): # 2.72+ nodeTexDiff.interpolation = 'Closest' # Graystyle Blending - nodeSaturateMix.inputs[0].default_value = 1.0 + nodeSaturateMix.inputs[saturateMixIn[0]].default_value = 1.0 nodeSaturateMix.blend_type = 'MULTIPLY' # changed from OVERLAY nodeSaturateMix.mute = True nodeSaturateMix.hide = True @@ -1645,6 +1654,14 @@ def matgen_cycles_original( nodeMixTrans.location = (1940, 0) nodeOut.location = (2140, 0) + # Mix RGB sockets for 3.4 + mixDiffIn = mixDiffSockets["inputs"] + mixDiffOut = mixDiffSockets["outputs"] + mixIn = mixSockets["inputs"] + mixOut = mixSockets["outputs"] + mixMetallicIn = mixMetallicSockets["inputs"] + mixMetallicOut = mixMetallicSockets["outputs"] + # Sets default transparency value nodeMixTrans.inputs["Fac"].default_value = 1 nodeMathMultiplyDiff.inputs[1].default_value = 0.1 @@ -1658,7 +1675,7 @@ def matgen_cycles_original( nodeMathPowerDiff.inputs[1].default_value = 2 nodeMathPower.inputs[1].default_value = 2 nodeMathMetallic.inputs[1].default_value = 4 - nodeMixRGBDiff.inputs["Color2"].default_value = [1, 1, 1, 1] + nodeMixRGBDiff.inputs[mixDiffIn[2]].default_value = [1, 1, 1, 1] # Sets default reflective values if use_reflections and checklist(canon, "reflective"): @@ -1686,23 +1703,23 @@ def matgen_cycles_original( # Connect nodes links.new(nodeMixDiff.outputs["Shader"], nodeMixMetallic.inputs[1]) - links.new(nodeMathPower.outputs[0], nodeMixRGB.inputs["Fac"]) + links.new(nodeMathPower.outputs[0], nodeMixRGB.inputs[mixIn[0]]) links.new(nodeMathPower.outputs[0], nodeDiff.inputs["Roughness"]) - links.new(nodeMixRGB.outputs[0], nodeFresnel.inputs["Normal"]) - links.new(nodeFresnel.outputs[0], nodeMixRGBDiff.inputs["Fac"]) - links.new(nodeGeometry.outputs["Incoming"], nodeMixRGB.inputs["Color2"]) - links.new(nodeBump.outputs["Normal"], nodeMixRGB.inputs["Color1"]) - links.new(nodeMathPowerDiff.outputs["Value"], nodeMixRGBDiff.inputs["Color1"]) - links.new(nodeMixRGBDiff.outputs["Color"], nodeMathMultiplyDiff.inputs[0]) + links.new(nodeMixRGB.outputs[mixOut[0]], nodeFresnel.inputs["Normal"]) + links.new(nodeFresnel.outputs[0], nodeMixRGBDiff.inputs[mixDiffIn[0]]) + links.new(nodeGeometry.outputs["Incoming"], nodeMixRGB.inputs[mixIn[2]]) + links.new(nodeBump.outputs["Normal"], nodeMixRGB.inputs[mixIn[1]]) + links.new(nodeMathPowerDiff.outputs["Value"], nodeMixRGBDiff.inputs[mixDiffIn[1]]) + links.new(nodeMixRGBDiff.outputs[mixDiffOut[0]], nodeMathMultiplyDiff.inputs[0]) links.new(nodeMathMultiplyDiff.outputs["Value"], nodeMixDiff.inputs["Fac"]) links.new(nodeDiff.outputs["BSDF"], nodeMixDiff.inputs[1]) links.new(nodeGlossDiff.outputs["BSDF"], nodeMixDiff.inputs[2]) links.new( - nodeFresnelMetallic.outputs["Fac"], nodeMixRGBMetallic.inputs["Fac"]) + nodeFresnelMetallic.outputs["Fac"], nodeMixRGBMetallic.inputs[mixMetallicIn[0]]) links.new( - nodeMathMetallic.outputs["Value"], nodeMixRGBMetallic.inputs["Color2"]) + nodeMathMetallic.outputs["Value"], nodeMixRGBMetallic.inputs[mixMetallicIn[2]]) links.new( - nodeMixRGBMetallic.outputs["Color"], nodeGlossMetallic.inputs["Color"]) + nodeMixRGBMetallic.outputs[mixMetallicOut[0]], nodeGlossMetallic.inputs["Color"]) links.new(nodeGlossMetallic.outputs["BSDF"], nodeMixMetallic.inputs[2]) links.new(nodeMixMetallic.outputs["Shader"], nodeMixEmit.inputs[1]) links.new(nodeMixCam.outputs["Shader"], nodeMixEmit.inputs[2]) @@ -1716,7 +1733,7 @@ def matgen_cycles_original( nodeInputs = [ [ - nodeMixRGBMetallic.inputs["Color1"], + nodeMixRGBMetallic.inputs[mixMetallicIn[1]], nodeMathMetallic.inputs[0], nodeDiff.inputs["Color"], nodeEmit.inputs["Color"], From d028fccdc59a29ff1d451fc4eddc7f9b7ceb9358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Wed, 7 Dec 2022 14:06:13 +0700 Subject: [PATCH 012/103] fix inputs,outputs --- MCprep_addon/materials/generate.py | 39 +++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 28329d58..624fb31a 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -960,23 +960,25 @@ def set_saturation_material(mat): def create_node(tree_nodes, node_type): """Function to create node""" - sockets= { - "inputs": inputs, - "outputs": outputs - } if node_type == 'ShaderNodeMixRGB': # For MixRGB in 3.4 become Legacy if bpy.app.version >= (3, 4, 0): node = tree_nodes.new('ShaderNodeMix') node.data_type = 'RGBA' - inputs = [0,5,6] + inputs = [0,6,7] outputs = [2] else: node = tree_nodes.new('ShaderNodeMixRGB') inputs = [0,1,2] outputs = [0] + sockets= { + "inputs": inputs, + "outputs": outputs + } + return node, sockets else: node = tree_nodes.new(node_type) - return node, sockets + return node + # ----------------------------------------------------------------------------- # Generating node groups @@ -1151,9 +1153,9 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): else: conf.log("Texture desaturated: " + canon, vv_only=True) desat_color = conf.json_data['blocks']['desaturated'][canon] - if len(desat_color) < len(nodeSaturateMix.inputs[2].default_value): + if len(desat_color) < len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value): desat_color.append(1.0) - nodeSaturateMix.inputs[2].default_value = desat_color + nodeSaturateMix.inputs[saturateMixIn[2]].default_value = desat_color nodeSaturateMix.mute = False nodeSaturateMix.hide = False @@ -1287,9 +1289,9 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): else: conf.log("Texture desaturated: " + canon, vv_only=True) desat_color = conf.json_data['blocks']['desaturated'][canon] - if len(desat_color) < len(nodeSaturateMix.inputs[2].default_value): + if len(desat_color) < len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value): desat_color.append(1.0) - nodeSaturateMix.inputs[2].default_value = desat_color + nodeSaturateMix.inputs[saturateMixIn[2]].default_value = desat_color nodeSaturateMix.mute = False nodeSaturateMix.hide = False @@ -1359,11 +1361,12 @@ def matgen_cycles_simple( principled.inputs["Metallic"].default_value = 0 saturateMixIn = sockets["inputs"] + saturateMixOut = sockets["outputs"] # Specular tends to cause some issues with how blocks look, so let's disable it principled.inputs["Specular"].default_value = 0 # Connect nodes. - links.new(nodeSaturateMix.outputs[0], principled.inputs[0]) + links.new(nodeSaturateMix.outputs[saturateMixOut[0]], principled.inputs[0]) links.new(principled.outputs["BSDF"], node_out.inputs[0]) if only_solid is True or checklist(canon, "solid"): @@ -1384,7 +1387,7 @@ def matgen_cycles_simple( inputs = [inp.name for inp in principled.inputs] if 'Emission Strength' in inputs: # Later 2.9 versions only. principled.inputs['Emission Strength'].default_value = 1 - links.new(nodeSaturateMix.outputs[0], principled.inputs["Emission"]) + links.new(nodeSaturateMix.outputs[saturateMixOut[0]], principled.inputs["Emission"]) # reapply animation data if any to generated nodes apply_texture_animation_pass_settings(mat, animated_data) @@ -1406,9 +1409,9 @@ def matgen_cycles_simple( else: conf.log("Texture desaturated: " + canon, vv_only=True) desat_color = conf.json_data['blocks']['desaturated'][canon] - if len(desat_color) < len(nodeSaturateMix.inputs[2].default_value): + if len(desat_color) < len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value): desat_color.append(1.0) - nodeSaturateMix.inputs[2].default_value = desat_color + nodeSaturateMix.inputs[saturateMixIn[2]].default_value = desat_color nodeSaturateMix.mute = False nodeSaturateMix.hide = False @@ -1417,7 +1420,7 @@ def matgen_cycles_simple( nodeSaturateMix["SATURATE"] = True nodeTexDiff.image = image_diff - links.new(nodeTexDiff.outputs[0], nodeSaturateMix.inputs[1]) + links.new(nodeTexDiff.outputs[0], nodeSaturateMix.inputs[saturateMixIn[1]]) return 0 @@ -1860,6 +1863,8 @@ def matgen_special_water(mat, passes): nodeTexNorm.label = "Normal Tex" nodeNormalInv.label = "Normal Inverse" + saturateMixIn = sockets["inputs"] + saturateMixOut = sockets["outputs"] # Sets default values nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) @@ -1933,9 +1938,9 @@ def matgen_special_water(mat, passes): else: conf.log("Texture desaturated: " + canon, vv_only=True) desat_color = conf.json_data['blocks']['desaturated'][canon] - if len(desat_color) < len(nodeSaturateMix.inputs[2].default_value): + if len(desat_color) < len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value): desat_color.append(1.0) - nodeSaturateMix.inputs[2].default_value = desat_color + nodeSaturateMix.inputs[saturateMixIn[2]].default_value = desat_color nodeSaturateMix.mute = False nodeSaturateMix.hide = False From 33c32475bd6b454aa1d37d5b254b858e1a8521f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Wed, 7 Dec 2022 22:19:12 +0700 Subject: [PATCH 013/103] use `util.min_bv` --- MCprep_addon/materials/generate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 624fb31a..2fe6002b 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -961,7 +961,7 @@ def set_saturation_material(mat): def create_node(tree_nodes, node_type): """Function to create node""" if node_type == 'ShaderNodeMixRGB': # For MixRGB in 3.4 become Legacy - if bpy.app.version >= (3, 4, 0): + if util.min_bv((3, 4, 0)): node = tree_nodes.new('ShaderNodeMix') node.data_type = 'RGBA' inputs = [0,6,7] @@ -971,8 +971,8 @@ def create_node(tree_nodes, node_type): inputs = [0,1,2] outputs = [0] sockets= { - "inputs": inputs, - "outputs": outputs + "inputs": inputs, + "outputs": outputs } return node, sockets else: From 680425ed5deeee918527190d87ba9b0283af5af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Thu, 8 Dec 2022 10:48:33 +0700 Subject: [PATCH 014/103] add `get_sockets_node`, improve `create_node` --- MCprep_addon/materials/generate.py | 70 ++++++++++++++++-------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 2fe6002b..69c08ee7 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -958,27 +958,33 @@ def set_saturation_material(mat): sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) -def create_node(tree_nodes, node_type): +def create_node(tree_nodes, node_type, **kwargs): """Function to create node""" if node_type == 'ShaderNodeMixRGB': # For MixRGB in 3.4 become Legacy if util.min_bv((3, 4, 0)): node = tree_nodes.new('ShaderNodeMix') node.data_type = 'RGBA' - inputs = [0,6,7] - outputs = [2] else: node = tree_nodes.new('ShaderNodeMixRGB') - inputs = [0,1,2] - outputs = [0] - sockets= { - "inputs": inputs, - "outputs": outputs - } - return node, sockets else: node = tree_nodes.new(node_type) - return node + for attr, value in kwargs.items(): + if hasattr(node, attr): + setattr(node, attr, value) + return node +def get_sockets_node(node, is_input=True): + n_type = node.bl_idname + if n_type == 'ShaderNodeMix' or n_type == 'ShaderNodeMixRGB': + if util.min_bv((3, 4, 0)): # For MixRGB in 3.4 become Legacy + if node.data_type == 'RGBA': + inputs, outputs = [0,6,7], [2] + else: + inputs, outputs = [0,1,2], [0] + else: + inputs, outputs = [i for i in range(len(node.inputs))], [i for i in range(len(node.outputs))] + print(inputs, outputs) + return inputs if is_input else outputs # ----------------------------------------------------------------------------- # Generating node groups @@ -1067,7 +1073,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") nodeSpecInv = create_node(nodes, "ShaderNodeInvert") - nodeSaturateMix, sockets = create_node(nodes, "ShaderNodeMixRGB") + nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB") nodeNormal = create_node(nodes, "ShaderNodeNormalMap") nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") @@ -1089,8 +1095,8 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) - saturateMixIn = sockets["inputs"] - saturateMixOut = sockets["outputs"] + saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] + saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] # Positions the nodes nodeTexDiff.location = (-380, 140) nodeTexNorm.location = (-680, -500) @@ -1190,7 +1196,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") nodeSpecInv = create_node(nodes, "ShaderNodeInvert") nodeSeperate = create_node(nodes, "ShaderNodeSeparateRGB") - nodeSaturateMix, sockets = create_node(nodes, "ShaderNodeMixRGB") + nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB") nodeNormal = create_node(nodes, "ShaderNodeNormalMap") nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") @@ -1214,8 +1220,8 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) - saturateMixIn = sockets["inputs"] - saturateMixOut = sockets["outputs"] + saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] + saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] # Positions the nodes nodeTexDiff.location = (-380, 140) nodeTexSpec.location = (-580, -180) @@ -1335,7 +1341,7 @@ def matgen_cycles_simple( nodes.clear() nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") - nodeSaturateMix, sockets = create_node(nodes, "ShaderNodeMixRGB") + nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB") nodeSaturateMix.name = "Add Color" nodeSaturateMix.label = "Add Color" principled = create_node(nodes, "ShaderNodeBsdfPrincipled") @@ -1360,8 +1366,8 @@ def matgen_cycles_simple( else: principled.inputs["Metallic"].default_value = 0 - saturateMixIn = sockets["inputs"] - saturateMixOut = sockets["outputs"] + saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] + saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] # Specular tends to cause some issues with how blocks look, so let's disable it principled.inputs["Specular"].default_value = 0 @@ -1612,9 +1618,9 @@ def matgen_cycles_original( nodeMixEmit = create_node(nodes, "ShaderNodeMixShader") nodeMixCam = create_node(nodes, "ShaderNodeMixShader") # Mix - nodeMixRGBDiff, mixDiffSockets = create_node(nodes, "ShaderNodeMixRGB") - nodeMixRGB, mixSockets = create_node(nodes, "ShaderNodeMixRGB") - nodeMixRGBMetallic, mixMetallicSockets = create_node(nodes, "ShaderNodeMixRGB") + nodeMixRGBDiff = create_node(nodes, "ShaderNodeMixRGB") + nodeMixRGB = create_node(nodes, "ShaderNodeMixRGB") + nodeMixRGBMetallic = create_node(nodes, "ShaderNodeMixRGB") # Input nodeFresnel = create_node(nodes, "ShaderNodeFresnel") nodeFresnelMetallic = create_node(nodes, "ShaderNodeFresnel") @@ -1658,12 +1664,12 @@ def matgen_cycles_original( nodeOut.location = (2140, 0) # Mix RGB sockets for 3.4 - mixDiffIn = mixDiffSockets["inputs"] - mixDiffOut = mixDiffSockets["outputs"] - mixIn = mixSockets["inputs"] - mixOut = mixSockets["outputs"] - mixMetallicIn = mixMetallicSockets["inputs"] - mixMetallicOut = mixMetallicSockets["outputs"] + mixDiffIn = get_sockets_node(nodeMixRGBDiff) #mixDiffSockets["inputs"] + mixDiffOut = get_sockets_node(nodeMixRGBDiff, is_input=False) #mixDiffSockets["outputs"] + mixIn = get_sockets_node(nodeMixRGB) #mixSockets["inputs"] + mixOut = get_sockets_node(nodeMixRGB, is_input=False) #mixSockets["outputs"] + mixMetallicIn = get_sockets_node(nodeMixRGBMetallic) #mixMetallicSockets["inputs"] + mixMetallicOut = get_sockets_node(nodeMixRGBMetallic, is_input=False) #mixMetallicSockets["outputs"] # Sets default transparency value nodeMixTrans.inputs["Fac"].default_value = 1 @@ -1838,7 +1844,7 @@ def matgen_special_water(mat, passes): nodeNormal = create_node(nodes,'ShaderNodeNormalMap') nodeNormalInv = create_node(nodes,'ShaderNodeRGBCurve') nodeBrightContrast = create_node(nodes,'ShaderNodeBrightContrast') - nodeSaturateMix, sockets = create_node(nodes,'ShaderNodeMixRGB') + nodeSaturateMix = create_node(nodes,'ShaderNodeMixRGB') nodeGlass = create_node(nodes,'ShaderNodeBsdfGlass') nodeTrans = create_node(nodes,'ShaderNodeBsdfTransparent') nodeMixTrans = create_node(nodes,'ShaderNodeMixShader') @@ -1863,8 +1869,8 @@ def matgen_special_water(mat, passes): nodeTexNorm.label = "Normal Tex" nodeNormalInv.label = "Normal Inverse" - saturateMixIn = sockets["inputs"] - saturateMixOut = sockets["outputs"] + saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] + saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] # Sets default values nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) From 3208306e3d96968f4d21fec5421d70751153f321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Thu, 8 Dec 2022 10:50:10 +0700 Subject: [PATCH 015/103] cleanup #1 --- MCprep_addon/materials/generate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 69c08ee7..495f1a0d 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -983,7 +983,6 @@ def get_sockets_node(node, is_input=True): inputs, outputs = [0,1,2], [0] else: inputs, outputs = [i for i in range(len(node.inputs))], [i for i in range(len(node.outputs))] - print(inputs, outputs) return inputs if is_input else outputs # ----------------------------------------------------------------------------- From d23d2423ec67617ab3189d8f6df96b6491ceac3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Thu, 8 Dec 2022 10:51:22 +0700 Subject: [PATCH 016/103] cleanup 1 --- MCprep_addon/materials/generate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 69c08ee7..495f1a0d 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -983,7 +983,6 @@ def get_sockets_node(node, is_input=True): inputs, outputs = [0,1,2], [0] else: inputs, outputs = [i for i in range(len(node.inputs))], [i for i in range(len(node.outputs))] - print(inputs, outputs) return inputs if is_input else outputs # ----------------------------------------------------------------------------- From 0f0e3b3cbac002d732ce46db20a4d4774d1def95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Fri, 9 Dec 2022 19:14:20 +0700 Subject: [PATCH 017/103] rename `get_node_socket` for consistency cleanup, commenting --- MCprep_addon/materials/generate.py | 76 ++++++++++++++++++------------ 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 495f1a0d..aff4c06d 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -958,9 +958,14 @@ def set_saturation_material(mat): sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) -def create_node(tree_nodes, node_type, **kwargs): - """Function to create node""" - if node_type == 'ShaderNodeMixRGB': # For MixRGB in 3.4 become Legacy +def create_node(tree_nodes, node_type, **attrs): + """Create node with default attributes + Args: + tree_nodes: the material node tree's nodes + node_type: the type of the node + **attrs: set attributes if that node type has (eg: location, name, blend_type...) + """ + if node_type == 'ShaderNodeMixRGB': # MixRGB in 3.4 if util.min_bv((3, 4, 0)): node = tree_nodes.new('ShaderNodeMix') node.data_type = 'RGBA' @@ -968,20 +973,23 @@ def create_node(tree_nodes, node_type, **kwargs): node = tree_nodes.new('ShaderNodeMixRGB') else: node = tree_nodes.new(node_type) - for attr, value in kwargs.items(): + for attr, value in attrs.items(): if hasattr(node, attr): setattr(node, attr, value) return node - -def get_sockets_node(node, is_input=True): + +def get_node_socket(node, is_input=True): + """Get the input or output sockets indicies for node""" n_type = node.bl_idname if n_type == 'ShaderNodeMix' or n_type == 'ShaderNodeMixRGB': - if util.min_bv((3, 4, 0)): # For MixRGB in 3.4 become Legacy + # Mix Color in 3.4 use socket indicies 0, 6, 7 for Factor, Color inputs and output with 2. + # MixRGB uses 0, 1, 2 and 0 + if util.min_bv((3, 4, 0)): if node.data_type == 'RGBA': - inputs, outputs = [0,6,7], [2] - else: + inputs, outputs = [0,6,7], [2] + else: inputs, outputs = [0,1,2], [0] - else: + else: inputs, outputs = [i for i in range(len(node.inputs))], [i for i in range(len(node.outputs))] return inputs if is_input else outputs @@ -1066,7 +1074,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): image_norm = passes["normal"] image_spec = passes["specular"] - # Creates the neccecary nodes + # Creates the necessary nodes nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") nodeTexNorm = create_node(nodes, "ShaderNodeTexImage") nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") @@ -1076,7 +1084,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeNormal = create_node(nodes, "ShaderNodeNormalMap") nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") - # Names and labels the neccecary nodes + # Names and labels the necessary nodes nodeTexDiff.name = "Diffuse Texture" nodeTexDiff.label = "Diffuse Texture" nodeTexNorm.name = "Normal Texture" @@ -1094,8 +1102,10 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) - saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] - saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] + # Get MixRGB sockets + saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs + # Positions the nodes nodeTexDiff.location = (-380, 140) nodeTexNorm.location = (-680, -500) @@ -1123,13 +1133,13 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): for i in nodeInputs[6]: links.new(nodeNormal.outputs["Normal"], i) - # Mutes neccacary nodes if no specular map + # Mutes necessary nodes if no specular map if image_spec: nodeTexSpec.image = image_spec else: nodeTexSpec.mute = True - # Mutes neccacary nodes if no normal map + # Mutes necessary nodes if no normal map if image_norm: nodeTexNorm.image = image_norm else: @@ -1189,7 +1199,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): image_norm = passes["normal"] image_spec = passes["specular"] - # Creates the neccecary nodes + # Creates the necessary nodes nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") nodeTexNorm = create_node(nodes, "ShaderNodeTexImage") nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") @@ -1199,7 +1209,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormal = create_node(nodes, "ShaderNodeNormalMap") nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") - # Names and labels the neccecary nodes + # Names and labels the necessary nodes nodeTexDiff.name = "Diffuse Texture" nodeTexDiff.label = "Diffuse Texture" nodeTexNorm.name = "Normal Texture" @@ -1219,8 +1229,10 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) - saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] - saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] + # Get MixRGB sockets + saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs + # Positions the nodes nodeTexDiff.location = (-380, 140) nodeTexSpec.location = (-580, -180) @@ -1365,8 +1377,10 @@ def matgen_cycles_simple( else: principled.inputs["Metallic"].default_value = 0 - saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] - saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] + # Get MixRGB sockets + saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs + # Specular tends to cause some issues with how blocks look, so let's disable it principled.inputs["Specular"].default_value = 0 @@ -1663,12 +1677,12 @@ def matgen_cycles_original( nodeOut.location = (2140, 0) # Mix RGB sockets for 3.4 - mixDiffIn = get_sockets_node(nodeMixRGBDiff) #mixDiffSockets["inputs"] - mixDiffOut = get_sockets_node(nodeMixRGBDiff, is_input=False) #mixDiffSockets["outputs"] - mixIn = get_sockets_node(nodeMixRGB) #mixSockets["inputs"] - mixOut = get_sockets_node(nodeMixRGB, is_input=False) #mixSockets["outputs"] - mixMetallicIn = get_sockets_node(nodeMixRGBMetallic) #mixMetallicSockets["inputs"] - mixMetallicOut = get_sockets_node(nodeMixRGBMetallic, is_input=False) #mixMetallicSockets["outputs"] + mixDiffIn = get_node_socket(nodeMixRGBDiff) # socket inputs + mixDiffOut = get_node_socket(nodeMixRGBDiff, is_input=False) # socket outputs + mixIn = get_node_socket(nodeMixRGB) # socket inputs + mixOut = get_node_socket(nodeMixRGB, is_input=False) # socket outputs + mixMetallicIn = get_node_socket(nodeMixRGBMetallic) # socket inputs + mixMetallicOut = get_node_socket(nodeMixRGBMetallic, is_input=False) # socket outputs # Sets default transparency value nodeMixTrans.inputs["Fac"].default_value = 1 @@ -1868,8 +1882,8 @@ def matgen_special_water(mat, passes): nodeTexNorm.label = "Normal Tex" nodeNormalInv.label = "Normal Inverse" - saturateMixIn = get_sockets_node(nodeSaturateMix) #sockets["inputs"] - saturateMixOut = get_sockets_node(nodeSaturateMix, is_input=False) #sockets["outputs"] + saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs # Sets default values nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) @@ -1882,7 +1896,7 @@ def matgen_special_water(mat, passes): # Connect nodes links.new(nodeTexDiff.outputs[0], nodeBrightContrast.inputs[0]) links.new(nodeBrightContrast.outputs[0], nodeSaturateMix.inputs[1]) - links.new(nodeSaturateMix.outputs[0], nodeGlass.inputs[0]) + links.new(nodeSaturateMix.outputs[saturateMixOut[0]], nodeGlass.inputs[0]) links.new(nodeGlass.outputs[0], nodeMixTrans.inputs[2]) links.new(nodeTrans.outputs[0], nodeMixTrans.inputs[1]) links.new(nodeMixTrans.outputs[0], nodeOut.inputs[0]) From 334b02a01fb78dd4aca676e8e0775fefd612dde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Fri, 9 Dec 2022 21:03:42 +0700 Subject: [PATCH 018/103] missing comment --- MCprep_addon/materials/generate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index aff4c06d..9edcca8a 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1882,6 +1882,7 @@ def matgen_special_water(mat, passes): nodeTexNorm.label = "Normal Tex" nodeNormalInv.label = "Normal Inverse" + # Mix RGB sockets for 3.4 saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs # Sets default values From df1bb1df5101d77e8f3713e557a2a60d209a35ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Fri, 9 Dec 2022 22:21:32 +0700 Subject: [PATCH 019/103] clean up 2 replace `location` `name` `label` any default attribute add `hide_sockets` optional hide the node sockets --- MCprep_addon/materials/generate.py | 364 +++++++++-------------------- 1 file changed, 105 insertions(+), 259 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 9edcca8a..3c597a6c 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -964,6 +964,7 @@ def create_node(tree_nodes, node_type, **attrs): tree_nodes: the material node tree's nodes node_type: the type of the node **attrs: set attributes if that node type has (eg: location, name, blend_type...) + "hide_sockets" to hide the sockets only display linked when need """ if node_type == 'ShaderNodeMixRGB': # MixRGB in 3.4 if util.min_bv((3, 4, 0)): @@ -976,6 +977,9 @@ def create_node(tree_nodes, node_type, **attrs): for attr, value in attrs.items(): if hasattr(node, attr): setattr(node, attr, value) + elif attr == 'hide_sockets': # option to hide socket for big node + node.inputs.foreach_set('hide', [value] * len(node.inputs)) + node.outputs.foreach_set('hide', [value] * len(node.outputs)) return node def get_node_socket(node, is_input=True): @@ -1075,30 +1079,21 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the necessary nodes - nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") - nodeTexNorm = create_node(nodes, "ShaderNodeTexImage") - nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") - - nodeSpecInv = create_node(nodes, "ShaderNodeInvert") - nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB") - nodeNormal = create_node(nodes, "ShaderNodeNormalMap") - nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") - - # Names and labels the necessary nodes - nodeTexDiff.name = "Diffuse Texture" - nodeTexDiff.label = "Diffuse Texture" - nodeTexNorm.name = "Normal Texture" - nodeTexNorm.label = "Normal Texture" - nodeTexSpec.name = "Specular Texture" - nodeTexSpec.label = "Specular Texture" - nodeSpecInv.name = "Specular Inverse" - nodeSpecInv.label = "Specular Inverse" - nodeSaturateMix.name = "Add Color" - nodeSaturateMix.label = "Add Color" - nodeNormalInv.name = "Normal Inverse" - nodeNormalInv.label = "Normal Inverse" + nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", + location = (-380, 140),interpolation = 'Closest') + nodeTexNorm = create_node(nodes, "ShaderNodeTexImage", name = "Normal Texture", label = "Normal Texture", + location = (-680, -500), interpolation = 'Closest') + nodeTexSpec = create_node(nodes, "ShaderNodeTexImage", name = "Specular Texture", label = "Specular Texture", + location = (-380, -180), interpolation = 'Closest') + nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", + location = (-80, 140), blend_type = 'MULTIPLY', mute = True, hide = True) + + nodeSpecInv = create_node(nodes, "ShaderNodeInvert", name = "Specular Inverse", label = "Specular Inverse", location = (-80, -280)) + nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve", name = "Normal Inverse", label = "Normal Inverse", location = (-380, -500)) + nodeNormal = create_node(nodes, "ShaderNodeNormalMap", location = (-80, 140)) # Sets values + nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) @@ -1106,15 +1101,6 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs - # Positions the nodes - nodeTexDiff.location = (-380, 140) - nodeTexNorm.location = (-680, -500) - nodeTexSpec.location = (-380, -180) - nodeSpecInv.location = (-80, -280) - nodeSaturateMix.location = (-80, 140) - nodeNormal.location = (-80, -500) - nodeNormalInv.location = (-380, -500) - # Links the nodes to the reroute nodes. links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) links.new(nodeTexNorm.outputs["Color"], nodeNormalInv.inputs["Color"]) @@ -1147,20 +1133,10 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mute = True nodeNormal.mute = True - # Sets to closest instead of linear interpolation - if hasattr(nodeTexDiff, "interpolation"): # 2.72+ - nodeTexDiff.interpolation = 'Closest' - nodeTexSpec.interpolation = 'Closest' - # Update to use non-color data for spec and normal util.apply_colorspace(nodeTexSpec, 'Non-Color') util.apply_colorspace(nodeTexNorm, 'Non-Color') - # Graystyle Blending - nodeSaturateMix.inputs[0].default_value = 1.0 - nodeSaturateMix.blend_type = 'MULTIPLY' # changed from OVERLAY - nodeSaturateMix.mute = True - nodeSaturateMix.hide = True if not checklist(canon, "desaturated"): pass elif not is_image_grayscale(image_diff): @@ -1200,32 +1176,22 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the necessary nodes - nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") - nodeTexNorm = create_node(nodes, "ShaderNodeTexImage") - nodeTexSpec = create_node(nodes, "ShaderNodeTexImage") - nodeSpecInv = create_node(nodes, "ShaderNodeInvert") - nodeSeperate = create_node(nodes, "ShaderNodeSeparateRGB") - nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB") - nodeNormal = create_node(nodes, "ShaderNodeNormalMap") - nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve") - - # Names and labels the necessary nodes - nodeTexDiff.name = "Diffuse Texture" - nodeTexDiff.label = "Diffuse Texture" - nodeTexNorm.name = "Normal Texture" - nodeTexNorm.label = "Normal Texture" - nodeTexSpec.name = "Specular Texture" - nodeTexSpec.label = "Specular Texture" - nodeSpecInv.name = "Smooth Inverse" - nodeSpecInv.label = "Smooth Inverse" - nodeSeperate.name = "RGB Seperation" - nodeSeperate.label = "RGB Seperation" - nodeSaturateMix.name = "Add Color" - nodeSaturateMix.label = "Add Color" - nodeNormalInv.name = "Normal Inverse" - nodeNormalInv.label = "Normal Inverse" + nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", + location = (-380, 140), interpolation = 'Closest') + nodeTexNorm = create_node(nodes, "ShaderNodeTexImage", name = "Normal Texture", label = "Normal Texture", + location = (-680, -500), interpolation = 'Closest') + nodeTexSpec = create_node(nodes, "ShaderNodeTexImage", name = "Specular Texture", label = "Specular Texture", + location = (-580, -180), interpolation = 'Closest') + nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", + location = (-80, 140), blend_type = 'MULTIPLY', mute = True, hide = True) + + nodeSpecInv = create_node(nodes, "ShaderNodeInvert", name = "Smooth Inverse", label = "Smooth Inverse", location = (-80, -280)) + nodeSeperate = create_node(nodes, "ShaderNodeSeparateRGB", name = "RGB Seperation", label = "RGB Seperation", location = (-280, -280)) + nodeNormal = create_node(nodes, "ShaderNodeNormalMap", location = (-80, -500)) + nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve", name = "Normal Inverse", label = "Normal Inverse", location = (-380, -500)) # Sets values + nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) @@ -1233,16 +1199,6 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs - # Positions the nodes - nodeTexDiff.location = (-380, 140) - nodeTexSpec.location = (-580, -180) - nodeTexNorm.location = (-680, -500) - nodeSeperate.location = (-280, -280) - nodeSpecInv.location = (-80, -280) - nodeSaturateMix.location = (-80, 140) - nodeNormal.location = (-80, -500) - nodeNormalInv.location = (-380, -500) - # Links the nodes to the reroute nodes. links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) links.new(nodeTexNorm.outputs["Color"], nodeNormalInv.inputs["Color"]) @@ -1264,7 +1220,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): for i in nodeInputs[6]: links.new(nodeNormal.outputs["Normal"], i) - # Mutes neccacary nodes if no specular map + # Mutes necessary nodes if no specular map if image_spec: nodeTexSpec.image = image_spec nodeTexSpec.mute = False @@ -1273,7 +1229,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeTexSpec.mute = True nodeSeperate.mute = True - # Mutes neccacary nodes if no normal map + # Mutes necessary nodes if no normal map if image_norm: nodeTexNorm.image = image_norm nodeTexNorm.mute = False @@ -1284,21 +1240,11 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mute = True nodeNormal.mute = True - # Sets to closest instead of linear interpolation - if hasattr(nodeTexDiff, "interpolation"): # 2.72+ - nodeTexDiff.interpolation = 'Closest' - nodeTexSpec.interpolation = 'Closest' - nodeTexNorm.interpolation = 'Closest' - # Update to use non-color data for spec and normal util.apply_colorspace(nodeTexSpec, 'Non-Color') util.apply_colorspace(nodeTexNorm, 'Non-Color') # Graystyle Blending - nodeSaturateMix.inputs[0].default_value = 1.0 - nodeSaturateMix.blend_type = 'MULTIPLY' # changed from OVERLAY - nodeSaturateMix.mute = True - nodeSaturateMix.hide = True if not checklist(canon, "desaturated"): pass elif not is_image_grayscale(image_diff): @@ -1351,17 +1297,13 @@ def matgen_cycles_simple( links = mat.node_tree.links nodes.clear() - nodeTexDiff = create_node(nodes, "ShaderNodeTexImage") - nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB") - nodeSaturateMix.name = "Add Color" - nodeSaturateMix.label = "Add Color" - principled = create_node(nodes, "ShaderNodeBsdfPrincipled") - node_out = create_node(nodes, "ShaderNodeOutputMaterial") + nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", + location = (0, 0), interpolation = 'Closest', image = image_diff) + nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", + location = (300, 0), blend_type = 'MULTIPLY', mute = True, hide = True) - # set location - nodeSaturateMix.location = (300, 0) - principled.location = (600, 0) - node_out.location = (900, 0) + principled = create_node(nodes, "ShaderNodeBsdfPrincipled", location = (600, 0)) + node_out = create_node(nodes, "ShaderNodeOutputMaterial", location = (900, 0)) # Sets default reflective values if use_reflections and checklist(canon, "reflective"): @@ -1381,10 +1323,12 @@ def matgen_cycles_simple( saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs - # Specular tends to cause some issues with how blocks look, so let's disable it + # Set values | Specular tends to cause some issues with how blocks look, so let's disable it + nodeSaturateMix.inputs[saturateMixIn[0]].default_value = 1.0 principled.inputs["Specular"].default_value = 0 # Connect nodes. + links.new(nodeTexDiff.outputs[0], nodeSaturateMix.inputs[saturateMixIn[1]]) links.new(nodeSaturateMix.outputs[saturateMixOut[0]], principled.inputs[0]) links.new(principled.outputs["BSDF"], node_out.inputs[0]) @@ -1411,16 +1355,7 @@ def matgen_cycles_simple( # reapply animation data if any to generated nodes apply_texture_animation_pass_settings(mat, animated_data) - # Now update texture image assignments. - nodeTexDiff.name = "Diffuse Texture" - # Sets to closest instead of linear interpolation. - if hasattr(nodeTexDiff, "interpolation"): # 2.72+ - nodeTexDiff.interpolation = 'Closest' # Graystyle Blending - nodeSaturateMix.inputs[saturateMixIn[0]].default_value = 1.0 - nodeSaturateMix.blend_type = 'MULTIPLY' # changed from OVERLAY - nodeSaturateMix.mute = True - nodeSaturateMix.hide = True if not checklist(canon, "desaturated"): pass elif not is_image_grayscale(image_diff): @@ -1437,9 +1372,6 @@ def matgen_cycles_simple( # annotate special nodes for finding later, and load images if available nodeTexDiff["MCPREP_diffuse"] = True nodeSaturateMix["SATURATE"] = True - nodeTexDiff.image = image_diff - - links.new(nodeTexDiff.outputs[0], nodeSaturateMix.inputs[saturateMixIn[1]]) return 0 @@ -1472,28 +1404,17 @@ def matgen_cycles_principled( links = mat.node_tree.links nodes.clear() - principled = create_node(nodes, "ShaderNodeBsdfPrincipled") - nodeEmit = create_node(nodes, "ShaderNodeEmission") - nodeEmitCam = create_node(nodes, "ShaderNodeEmission") - nodeMixCam = create_node(nodes, "ShaderNodeMixShader") - nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff") - nodeLightPath = create_node(nodes, "ShaderNodeLightPath") - nodeMixEmit = create_node(nodes, "ShaderNodeMixShader") - nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent") - nodeMixTrans = create_node(nodes, "ShaderNodeMixShader") - nodeOut = create_node(nodes, "ShaderNodeOutputMaterial") - - # set location - nodeEmit.location = (120, 140) - nodeEmitCam.location = (120, 260) - nodeFalloff.location = (-80, 320) - nodeLightPath.location = (-320, 520) - nodeMixCam.location = (320, 260) - nodeTrans.location = (420, 140) - nodeMixEmit.location = (420, 0) - nodeMixTrans.location = (620, 0) - nodeOut.location = (820, 0) - principled.location = (120, 0) + principled = create_node(nodes, "ShaderNodeBsdfPrincipled", location = (120, 0)) + nodeEmit = create_node(nodes, "ShaderNodeEmission", location = (120, 140)) + nodeEmitCam = create_node(nodes, "ShaderNodeEmission", location = (120, 260)) + nodeMixCam = create_node(nodes, "ShaderNodeMixShader", location = (320, 260)) + nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff", location = (-80, 320)) + nodeLightPath = create_node(nodes, "ShaderNodeLightPath", location = (-320, 520)) + nodeMixEmit = create_node(nodes, "ShaderNodeMixShader", location = (420, 0)) + nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent", location = (420, 140)) + nodeMixTrans = create_node(nodes, "ShaderNodeMixShader", location = (620, 0)) + nodeOut = create_node(nodes, "ShaderNodeOutputMaterial", location = (820, 0)) + # Sets default transparency value nodeMixTrans.inputs[0].default_value = 1 @@ -1618,63 +1539,36 @@ def matgen_cycles_original( nodes.clear() # Shader - nodeDiff = create_node(nodes, "ShaderNodeBsdfDiffuse") - nodeGlossDiff = create_node(nodes, "ShaderNodeBsdfGlossy") - nodeGlossMetallic = create_node(nodes, "ShaderNodeBsdfGlossy") - nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent") - nodeEmit = create_node(nodes, "ShaderNodeEmission") - nodeEmitCam = create_node(nodes, "ShaderNodeEmission") + nodeDiff = create_node(nodes, "ShaderNodeBsdfDiffuse", location = (940, 200)) + nodeGlossDiff = create_node(nodes, "ShaderNodeBsdfGlossy", location = (940, 60)) + nodeGlossMetallic = create_node(nodes, "ShaderNodeBsdfGlossy", location = (1140, -120)) + nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent", location = (1740, 120)) + nodeEmit = create_node(nodes, "ShaderNodeEmission", location = (1340, 120)) + nodeEmitCam = create_node(nodes, "ShaderNodeEmission", location = (1340, 240)) # Mix Shader - nodeMixDiff = create_node(nodes, "ShaderNodeMixShader") - nodeMixMetallic = create_node(nodes, "ShaderNodeMixShader") - nodeMixTrans = create_node(nodes, "ShaderNodeMixShader") - nodeMixEmit = create_node(nodes, "ShaderNodeMixShader") - nodeMixCam = create_node(nodes, "ShaderNodeMixShader") + nodeMixDiff = create_node(nodes, "ShaderNodeMixShader", location = (1140, 40)) + nodeMixMetallic = create_node(nodes, "ShaderNodeMixShader", location = (1340, 0)) + nodeMixTrans = create_node(nodes, "ShaderNodeMixShader", location = (1940, 0)) + nodeMixEmit = create_node(nodes, "ShaderNodeMixShader", location = (1740, 0)) + nodeMixCam = create_node(nodes, "ShaderNodeMixShader", location = (1540, 240)) # Mix - nodeMixRGBDiff = create_node(nodes, "ShaderNodeMixRGB") - nodeMixRGB = create_node(nodes, "ShaderNodeMixRGB") - nodeMixRGBMetallic = create_node(nodes, "ShaderNodeMixRGB") + nodeMixRGBDiff = create_node(nodes, "ShaderNodeMixRGB", location = (560, 200)) + nodeMixRGB = create_node(nodes, "ShaderNodeMixRGB", location = (180, 360)) + nodeMixRGBMetallic = create_node(nodes, "ShaderNodeMixRGB", location = (940, -120)) # Input - nodeFresnel = create_node(nodes, "ShaderNodeFresnel") - nodeFresnelMetallic = create_node(nodes, "ShaderNodeFresnel") - nodeLightPath = create_node(nodes, "ShaderNodeLightPath") - nodeGeometry = create_node(nodes, "ShaderNodeNewGeometry") + nodeFresnel = create_node(nodes, "ShaderNodeFresnel", location = (360, 160)) + nodeFresnelMetallic = create_node(nodes, "ShaderNodeFresnel", location = (740, -120)) + nodeLightPath = create_node(nodes, "ShaderNodeLightPath", location = (1340, 600)) + nodeGeometry = create_node(nodes, "ShaderNodeNewGeometry", location = (0, 600)) # Math - nodeMathPower = create_node(nodes, "ShaderNodeMath") - nodeMathPowerDiff = create_node(nodes, "ShaderNodeMath") - nodeMathMultiplyDiff = create_node(nodes, "ShaderNodeMath") - nodeMathMetallic = create_node(nodes, "ShaderNodeMath") + nodeMathPower = create_node(nodes, "ShaderNodeMath", location = (0, 360), operation = "POWER") + nodeMathPowerDiff = create_node(nodes, "ShaderNodeMath", location = (360, 360), operation = "POWER") + nodeMathMultiplyDiff = create_node(nodes, "ShaderNodeMath", location = (740, 200), operation = "MULTIPLY") + nodeMathMetallic = create_node(nodes, "ShaderNodeMath", location = (740, -280), operation = "MULTIPLY") # Etc - nodeBump = create_node(nodes, "ShaderNodeBump") - nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff") - nodeOut = create_node(nodes, "ShaderNodeOutputMaterial") - - # set location - nodeMixDiff.location = (1140, 40) - nodeMathMultiplyDiff.location = (740, 200) - nodeMixRGBDiff.location = (560, 200) - nodeMathPowerDiff.location = (360, 360) - nodeMixRGB.location = (180, 360) - nodeFresnel.location = (360, 160) - nodeMathPower.location = (0, 360) - nodeGeometry.location = (0, 600) - nodeBump.location = (-200, 600) - nodeDiff.location = (940, 200) - nodeGlossDiff.location = (940, 60) - nodeFresnelMetallic.location = (740, -120) - nodeMathMetallic.location = (740, -280) - nodeMixRGBMetallic.location = (940, -120) - nodeGlossMetallic.location = (1140, -120) - nodeMixMetallic.location = (1340, 0) - nodeFalloff.location = (1140, 240) - nodeLightPath.location = ((1340, 600)) - nodeEmit.location = (1340, 120) - nodeEmitCam.location = (1340, 240) - nodeMixCam.location = (1540, 240) - nodeMixEmit.location = (1740, 0) - nodeTrans.location = (1740, 120) - nodeMixTrans.location = (1940, 0) - nodeOut.location = (2140, 0) + nodeBump = create_node(nodes, "ShaderNodeBump", location = (-200, 600)) + nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff", location = (1140, 240)) + nodeOut = create_node(nodes, "ShaderNodeOutputMaterial", location = (2140, 0)) # Mix RGB sockets for 3.4 mixDiffIn = get_node_socket(nodeMixRGBDiff) # socket inputs @@ -1689,10 +1583,6 @@ def matgen_cycles_original( nodeMathMultiplyDiff.inputs[1].default_value = 0.1 nodeFalloff.inputs["Strength"].default_value = 32 nodeEmitCam.inputs["Strength"].default_value = 4 - nodeMathMetallic.operation = "POWER" - nodeMathPowerDiff.operation = "POWER" - nodeMathPower.operation = "POWER" - nodeMathMultiplyDiff.operation = "MULTIPLY" nodeMathPowerDiff.inputs[0].default_value = 0 nodeMathPowerDiff.inputs[1].default_value = 2 nodeMathPower.inputs[1].default_value = 2 @@ -1852,40 +1742,27 @@ def matgen_special_water(mat, passes): links = mat.node_tree.links nodes.clear() - nodeTexDiff = create_node(nodes,'ShaderNodeTexImage') - nodeTexNorm = create_node(nodes,'ShaderNodeTexImage') - nodeNormal = create_node(nodes,'ShaderNodeNormalMap') - nodeNormalInv = create_node(nodes,'ShaderNodeRGBCurve') - nodeBrightContrast = create_node(nodes,'ShaderNodeBrightContrast') - nodeSaturateMix = create_node(nodes,'ShaderNodeMixRGB') - nodeGlass = create_node(nodes,'ShaderNodeBsdfGlass') - nodeTrans = create_node(nodes,'ShaderNodeBsdfTransparent') - nodeMixTrans = create_node(nodes,'ShaderNodeMixShader') - nodeOut = create_node(nodes,'ShaderNodeOutputMaterial') - - # set location - nodeTexDiff.location = (-180, 140) - nodeTexNorm.location = (-290, -180) - nodeSaturateMix.location = (-80, 140) - nodeNormal.location = (310, -180) - nodeNormalInv.location = (10, -180) - nodeBrightContrast.location = (120, 140) - nodeSaturateMix.location = (320, 140) - nodeGlass.location = (520, 140) - nodeTrans.location = (520, 340) - nodeMixTrans.location = (720, 140) - nodeOut.location = (920, 140) - - nodeTexDiff.name = "Diffuse Tex" - nodeTexDiff.label = "Diffuse Tex" - nodeTexNorm.name = "Normal Tex" - nodeTexNorm.label = "Normal Tex" - nodeNormalInv.label = "Normal Inverse" + nodeTexDiff = create_node(nodes,'ShaderNodeTexImage', name = "Diffuse Texure", label = "Diffuse Texure", + location = (-180, 140), interpolation = 'Closest', image = image_diff) + nodeTexNorm = create_node(nodes,'ShaderNodeTexImage',name = "Normal Texure",label = "Normal Texure", + location = (-290, -180), interpolation = 'Closest') + nodeSaturateMix = create_node(nodes,'ShaderNodeMixRGB', name = "Add Color", label = "Add Color", + location = (320, 140), blend_type = 'MULTIPLY', mute = True, hide = True) + nodeNormalInv = create_node(nodes,'ShaderNodeRGBCurve',name = "Normal Inverse", label = "Normal Inverse", location = (10, -180)) + nodeNormal = create_node(nodes,'ShaderNodeNormalMap', location = (310, -180)) + nodeBrightContrast = create_node(nodes,'ShaderNodeBrightContrast', location = (120, 140)) + + nodeGlass = create_node(nodes,'ShaderNodeBsdfGlass', location = (520, 140)) + nodeTrans = create_node(nodes,'ShaderNodeBsdfTransparent', location = (520, 340)) + nodeMixTrans = create_node(nodes,'ShaderNodeMixShader', location = (720, 140)) + nodeOut = create_node(nodes,'ShaderNodeOutputMaterial', location = (920, 140)) # Mix RGB sockets for 3.4 saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs + # Sets default values + nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) nodeMixTrans.inputs[0].default_value = 0.8 @@ -1905,10 +1782,6 @@ def matgen_special_water(mat, passes): links.new(nodeNormalInv.outputs[0], nodeNormal.inputs[0]) links.new(nodeNormal.outputs[0], nodeGlass.inputs[3]) - # Sets to closest instead of linear interpolation - if hasattr(nodeTexDiff, "interpolation"): # 2.72+ - nodeTexDiff.interpolation = 'Closest' - # Normal update util.apply_colorspace(nodeTexNorm, 'Non-Color') if image_norm: @@ -1947,10 +1820,6 @@ def matgen_special_water(mat, passes): apply_texture_animation_pass_settings(mat, animated_data) # Graystyle Blending - nodeSaturateMix.inputs[0].default_value = 1.0 - nodeSaturateMix.blend_type = 'MULTIPLY' # changed from OVERLAY - nodeSaturateMix.mute = True - nodeSaturateMix.hide = True if not checklist(canon, "desaturated"): pass elif not is_image_grayscale(image_diff): @@ -1969,7 +1838,6 @@ def matgen_special_water(mat, passes): nodeTexNorm["MCPREP_normal"] = True nodeNormal["MCPREP_normal"] = True # to also be also muted if no normal tex # nodeTexDisp["MCPREP_disp"] = True - nodeTexDiff.image = image_diff return 0 @@ -2003,34 +1871,17 @@ def matgen_special_glass(mat, passes): links = mat.node_tree.links nodes.clear() - nodeDiff = create_node(nodes, 'ShaderNodeBsdfDiffuse') - nodeMixTrans = create_node(nodes, 'ShaderNodeMixShader') - nodeOut = create_node(nodes, 'ShaderNodeOutputMaterial') - nodeTexDiff = create_node(nodes, 'ShaderNodeTexImage') - nodeTexNorm = create_node(nodes, 'ShaderNodeTexImage') - nodeNormal = create_node(nodes, 'ShaderNodeNormalMap') - nodeNormalInv = create_node(nodes, 'ShaderNodeRGBCurve') - nodeGlass = create_node(nodes, 'ShaderNodeBsdfGlass') - nodeBrightContrast = create_node(nodes, 'ShaderNodeBrightContrast') - - # Names and labels the neccecary nodes - nodeTexDiff.name = "Diffuse Tex" - nodeTexDiff.label = "Diffuse Tex" - nodeTexNorm.name = "Normal Tex" - nodeTexNorm.label = "Normal Tex" - nodeNormalInv.label = "Normal Inverse" - - # Positions the nodes - nodeTexDiff.location = (-380, 140) - nodeTexNorm.location = (-680, -180) - nodeNormal.location = (-80, -180) - nodeNormalInv.location = (-380, -180) - nodeOut.location = (820, 0) - nodeDiff.location = (120, 0) - nodeGlass.location = (120, 240) - nodeMixTrans.location = (620, 0) - nodeOut.location = (820, 0) - nodeBrightContrast.location = (420, 0) + nodeTexDiff = create_node(nodes, 'ShaderNodeTexImage', name = "Diffuse Texture", label = "Diffuse Texture", + location = (-380, 140), interpolation = 'Closest', image = image_diff) + nodeTexNorm = create_node(nodes, 'ShaderNodeTexImage', name = "Normal Texture", label = "Normal Texture", + location = (-680, -180), interpolation = 'Closest') + nodeNormalInv = create_node(nodes, 'ShaderNodeRGBCurve', name = "Normal Inverse", label = "Normal Inverse", location = (-380, -180)) + nodeNormal = create_node(nodes, 'ShaderNodeNormalMap', location = (-80, -180)) + nodeDiff = create_node(nodes, 'ShaderNodeBsdfDiffuse', location = (120, 0)) + nodeMixTrans = create_node(nodes, 'ShaderNodeMixShader', location = (620, 0)) + nodeGlass = create_node(nodes, 'ShaderNodeBsdfGlass', location = (120, 240)) + nodeBrightContrast = create_node(nodes, 'ShaderNodeBrightContrast', location = (420, 0)) + nodeOut = create_node(nodes, 'ShaderNodeOutputMaterial', location = (820, 0)) # Sets default transparency value nodeMixTrans.inputs[0].default_value = 1 @@ -2052,10 +1903,6 @@ def matgen_special_glass(mat, passes): links.new(nodeNormalInv.outputs["Color"], nodeNormal.inputs["Color"]) links.new(nodeNormal.outputs[0], nodeDiff.inputs[2]) - # Sets to closest instead of linear interpolation - if hasattr(nodeTexDiff, "interpolation"): # 2.72+ - nodeTexDiff.interpolation = 'Closest' - # Normal update util.apply_colorspace(nodeTexNorm, 'Non-Color') if image_norm: @@ -2098,6 +1945,5 @@ def matgen_special_glass(mat, passes): nodeTexNorm["MCPREP_normal"] = True nodeNormal["MCPREP_normal"] = True # to also be also muted if no normal tex # nodeTexDisp["MCPREP_disp"] = True - nodeTexDiff.image = image_diff return 0 # return 0 once implemented From 78aa9c17be5f1c07c56f9321961dbdd69d3368f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Sat, 10 Dec 2022 00:28:10 +0700 Subject: [PATCH 020/103] cleanup comments --- MCprep_addon/materials/generate.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 3c597a6c..d7ae74a9 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1196,8 +1196,8 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) # Get MixRGB sockets - saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs - saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs + saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs # Links the nodes to the reroute nodes. links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) @@ -1320,8 +1320,8 @@ def matgen_cycles_simple( principled.inputs["Metallic"].default_value = 0 # Get MixRGB sockets - saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs - saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs + saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs # Set values | Specular tends to cause some issues with how blocks look, so let's disable it nodeSaturateMix.inputs[saturateMixIn[0]].default_value = 1.0 @@ -1570,13 +1570,13 @@ def matgen_cycles_original( nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff", location = (1140, 240)) nodeOut = create_node(nodes, "ShaderNodeOutputMaterial", location = (2140, 0)) - # Mix RGB sockets for 3.4 - mixDiffIn = get_node_socket(nodeMixRGBDiff) # socket inputs - mixDiffOut = get_node_socket(nodeMixRGBDiff, is_input=False) # socket outputs - mixIn = get_node_socket(nodeMixRGB) # socket inputs - mixOut = get_node_socket(nodeMixRGB, is_input=False) # socket outputs - mixMetallicIn = get_node_socket(nodeMixRGBMetallic) # socket inputs - mixMetallicOut = get_node_socket(nodeMixRGBMetallic, is_input=False) # socket outputs + # Get MixRGB sockets + mixDiffIn = get_node_socket(nodeMixRGBDiff) #socket inputs + mixIn = get_node_socket(nodeMixRGB) + mixMetallicIn = get_node_socket(nodeMixRGBMetallic) + mixOut = get_node_socket(nodeMixRGB, is_input=False) #socket outputs + mixDiffOut = get_node_socket(nodeMixRGBDiff, is_input=False) + mixMetallicOut = get_node_socket(nodeMixRGBMetallic, is_input=False) # Sets default transparency value nodeMixTrans.inputs["Fac"].default_value = 1 From 567a27c93aee39e95f617aca0ea7b10b2219a023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Sat, 10 Dec 2022 00:43:12 +0700 Subject: [PATCH 021/103] normal node at wrong location --- MCprep_addon/materials/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index d7ae74a9..5cf9957e 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1090,7 +1090,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeSpecInv = create_node(nodes, "ShaderNodeInvert", name = "Specular Inverse", label = "Specular Inverse", location = (-80, -280)) nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve", name = "Normal Inverse", label = "Normal Inverse", location = (-380, -500)) - nodeNormal = create_node(nodes, "ShaderNodeNormalMap", location = (-80, 140)) + nodeNormal = create_node(nodes, "ShaderNodeNormalMap", location = (-80, -500)) # Sets values nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending From ff274f82cabe9fa3ab1b9b5a7ed7adee2446b8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Sat, 10 Dec 2022 14:00:08 +0700 Subject: [PATCH 022/103] comment, update where `create_node` can use --- MCprep_addon/materials/generate.py | 2 +- MCprep_addon/materials/prep.py | 8 ++++---- MCprep_addon/spawner/item.py | 26 ++++++-------------------- MCprep_addon/world_tools.py | 25 +++++++++---------------- 4 files changed, 20 insertions(+), 41 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 5cf9957e..6fd00973 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1137,6 +1137,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): util.apply_colorspace(nodeTexSpec, 'Non-Color') util.apply_colorspace(nodeTexNorm, 'Non-Color') + # Graystyle Blending if not checklist(canon, "desaturated"): pass elif not is_image_grayscale(image_diff): @@ -1418,7 +1419,6 @@ def matgen_cycles_principled( # Sets default transparency value nodeMixTrans.inputs[0].default_value = 1 - nodeFalloff.inputs["Strength"].default_value = 32 nodeEmitCam.inputs["Strength"].default_value = 4 diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 7e1e4df4..65088228 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -574,14 +574,14 @@ def generate_base_material(self, context, name, path): elif engine == 'CYCLES' or engine == 'BLENDER_EEVEE': # need to create at least one texture node first, then the rest works mat.use_nodes = True - node_diff = mat.node_tree.nodes.new('ShaderNodeTexImage') - node_diff.image = image + nodes = mat.node_tree.nodes + node_diff = generate.create_node(nodes, 'ShaderNodeTexImage', image = image) node_diff["MCPREP_diffuse"] = True # Initialize extra passes as well - node_spec = mat.node_tree.nodes.new('ShaderNodeTexImage') + node_spec = generate.create_node(nodes, 'ShaderNodeTexImage') node_spec["MCPREP_specular"] = True - node_nrm = mat.node_tree.nodes.new('ShaderNodeTexImage') + node_nrm = generate.create_node(nodes, 'ShaderNodeTexImage') node_nrm["MCPREP_normal"] = True conf.log("Added blank texture node") diff --git a/MCprep_addon/spawner/item.py b/MCprep_addon/spawner/item.py index 66cbadbb..78cec9ec 100644 --- a/MCprep_addon/spawner/item.py +++ b/MCprep_addon/spawner/item.py @@ -25,7 +25,7 @@ from .. import conf from .. import util from .. import tracking - +from ..materials import generate try: import bpy.utils.previews except ImportError: @@ -256,36 +256,22 @@ def spawn_item_from_filepath( for node in nodes: nodes.remove(node) - diffuse_node = nodes.new(type="ShaderNodeBsdfDiffuse") - tex_node = nodes.new(type='ShaderNodeTexImage') - output_node = nodes.new(type='ShaderNodeOutputMaterial') - tex_node.image = image - tex_node.interpolation = 'Closest' + tex_node = generate.create_node(nodes, 'ShaderNodeTexImage', image = image, interpolation = 'Closest', location = (-400,0)) + diffuse_node = generate.create_node(nodes, "ShaderNodeBsdfDiffuse", location = (-200,-100)) + output_node = generate.create_node(nodes, 'ShaderNodeOutputMaterial', location = (200,0)) if transparency == 0: links.new(tex_node.outputs[0], diffuse_node.inputs[0]) links.new(diffuse_node.outputs[0], output_node.inputs[0]) - - diffuse_node.location[0] -= 200 - diffuse_node.location[1] -= 100 - tex_node.location[0] -= 400 - output_node.location[0] += 200 else: - transp_node = nodes.new(type='ShaderNodeBsdfTransparent') - mix_node = nodes.new(type='ShaderNodeMixShader') + transp_node = generate.create_node(nodes, 'ShaderNodeBsdfTransparent', location = (-200,100)) + mix_node = generate.create_node(nodes, 'ShaderNodeMixShader') links.new(tex_node.outputs[0], diffuse_node.inputs[0]) links.new(diffuse_node.outputs[0], mix_node.inputs[2]) links.new(transp_node.outputs[0], mix_node.inputs[1]) links.new(tex_node.outputs[1], mix_node.inputs[0]) links.new(mix_node.outputs[0], output_node.inputs[0]) - transp_node.location[0] -= 200 - transp_node.location[1] += 100 - diffuse_node.location[0] -= 200 - diffuse_node.location[1] -= 100 - tex_node.location[0] -= 400 - output_node.location[0] += 200 - # Final object updated if thickness > 0: mod = itm_obj.modifiers.new(type='SOLIDIFY', name='Solidify') diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index a67a5b93..88ba51cf 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -25,6 +25,7 @@ from . import conf from . import util from . import tracking +from .materials import generate # ----------------------------------------------------------------------------- @@ -470,12 +471,9 @@ def prep_world_cycles(self, context): if "mcprep_world" not in context.scene.world: world_nodes.clear() - skynode = world_nodes.new("ShaderNodeTexSky") - background = world_nodes.new("ShaderNodeBackground") - output = world_nodes.new("ShaderNodeOutputWorld") - skynode.location = (-280, 300) - background.location = (10, 300) - output.location = (300, 300) + skynode = generate.create_node(world_nodes, "ShaderNodeTexSky", location = (-280, 300)) + background = generate.create_node(world_nodes, "ShaderNodeBackground", location = (10, 300)) + output = generate.create_node(world_nodes, "ShaderNodeOutputWorld", location = (300, 300)) world_links.new(skynode.outputs["Color"], background.inputs[0]) world_links.new(background.outputs["Background"], output.inputs[0]) @@ -505,16 +503,11 @@ def prep_world_eevee(self, context): if "mcprep_world" not in context.scene.world: world_nodes.clear() - light_paths = world_nodes.new("ShaderNodeLightPath") - background_camera = world_nodes.new("ShaderNodeBackground") - background_others = world_nodes.new("ShaderNodeBackground") - mix_shader = world_nodes.new("ShaderNodeMixShader") - output = world_nodes.new("ShaderNodeOutputWorld") - light_paths.location = (-150, 400) - background_others.location = (10, 300) - background_camera.location = (10, 150) - mix_shader.location = (300, 300) - output.location = (500, 300) + light_paths = generate.create_node(world_nodes, "ShaderNodeLightPath", location = (-150, 400)) + background_camera = generate.create_node(world_nodes, "ShaderNodeBackground", location = (10, 150)) + background_others = generate.create_node(world_nodes, "ShaderNodeBackground", location = (10, 300)) + mix_shader =generate.create_node(world_nodes, "ShaderNodeMixShader", location = (300, 300)) + output = generate.create_node(world_nodes, "ShaderNodeOutputWorld", location = (500, 300)) background_others.inputs["Color"].default_value = (0.14965, 0.425823, 1, 1) background_others.inputs["Strength"].default_value = 0.1 background_camera.inputs["Color"].default_value = (0.14965, 0.425823, 1, 1) From 8c45c4c053e2f9624038969aecf29bc561c1ef55 Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Wed, 28 Dec 2022 21:52:32 -0600 Subject: [PATCH 023/103] Added info on installing bpy for IDEs --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3cebc3b..73a0c7d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -170,3 +170,8 @@ One other detail: MCprep uses git lfs or Large File Storage, to avoid saving bin - Alternatively, try using Git for Windows and its console. Run into other gotchas? Please open a [new issue](https://github.com/TheDuckCow/MCprep/issues)! + +## Installing `bpy` for IDEs +If you're using an IDE, it's recommened to install `bpy` as a Python module. In my (StandingPad) experiance, the [fake-bpy package](https://github.com/nutti/fake-bpy-module) seems to be the best. + +It's also recommened to use a virtual environment (especially if you're on Linux) as to avoid issues with system wide packages. [See this for more details](https://realpython.com/python-virtual-environments-a-primer/) From 81e12b4ca6519faa44b9e5d4f73ec87cda826347 Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:27:50 -0600 Subject: [PATCH 024/103] Added a section on setting up IDEs --- CONTRIBUTING.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73a0c7d4..c9c445b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -171,7 +171,29 @@ One other detail: MCprep uses git lfs or Large File Storage, to avoid saving bin Run into other gotchas? Please open a [new issue](https://github.com/TheDuckCow/MCprep/issues)! -## Installing `bpy` for IDEs +## IDE Support If you're using an IDE, it's recommened to install `bpy` as a Python module. In my (StandingPad) experiance, the [fake-bpy package](https://github.com/nutti/fake-bpy-module) seems to be the best. -It's also recommened to use a virtual environment (especially if you're on Linux) as to avoid issues with system wide packages. [See this for more details](https://realpython.com/python-virtual-environments-a-primer/) +It's also recommened to use a virtual environment (especially if you're on Linux) as to avoid issues with system wide packages and different versions of `bpy`. [See this for more details](https://realpython.com/python-virtual-environments-a-primer/) + +### Creating a Virtual Environment and Setting up `bpy` +First, we need to come up with a name. For MCprep development, it's recommended to use the following convention: +`mcprep_venv_` + +For example, if I was making a virtul environment for 3.3, I would do `mcprep_venv_3.3`. + +To create a virtual environment, do the following +`python3 -m venv mcprep_venv_` + +Then to enable it, then: +Windows: `venv\Scripts\activate` +MacOS and Linux: `source venv\Scripts\activate` + +This will make your terminal use the virtual environment until you close it or use `deactivate`. Each time you open your terminal after this, remember to enable the virtual environment + +Next we need to install `fake-bpy`: +`python3 -m pip install fake-bpy-module-` + +If you use PyCharm, you should check the GitHub for [additional instructions](https://github.com/nutti/fake-bpy-module#install-via-pip-package) + +Now you're ready to do MCprep development From 661764757abe092b610fb94f65911b3079881160 Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:29:00 -0600 Subject: [PATCH 025/103] Update CONTRIBUTING.md --- CONTRIBUTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9c445b8..223f027f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -182,16 +182,20 @@ First, we need to come up with a name. For MCprep development, it's recommended For example, if I was making a virtul environment for 3.3, I would do `mcprep_venv_3.3`. -To create a virtual environment, do the following +To create a virtual environment, do the following: + `python3 -m venv mcprep_venv_` Then to enable it, then: + Windows: `venv\Scripts\activate` + MacOS and Linux: `source venv\Scripts\activate` This will make your terminal use the virtual environment until you close it or use `deactivate`. Each time you open your terminal after this, remember to enable the virtual environment Next we need to install `fake-bpy`: + `python3 -m pip install fake-bpy-module-` If you use PyCharm, you should check the GitHub for [additional instructions](https://github.com/nutti/fake-bpy-module#install-via-pip-package) From d46de9c563c4e9c5568cb61253295ffe9aaef889 Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:30:05 -0600 Subject: [PATCH 026/103] Added virtual environments to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c31f5f32..51ed3dd7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ build test_results.tsv debug_save_state.blend1 MCprep_addon/MCprep_resources/resourcepacks/mcprep_default/materials.blend1 +mcprep_venv_* From 7ff33d510925e2b16a0691a414be4c03674a13f6 Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:31:55 -0600 Subject: [PATCH 027/103] fixed some mistakes --- CONTRIBUTING.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 223f027f..9f6a05af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -180,6 +180,8 @@ It's also recommened to use a virtual environment (especially if you're on Linux First, we need to come up with a name. For MCprep development, it's recommended to use the following convention: `mcprep_venv_` +This allows you to have multiple versions of `bpy` side by side in their own environments. + For example, if I was making a virtul environment for 3.3, I would do `mcprep_venv_3.3`. To create a virtual environment, do the following: @@ -188,9 +190,9 @@ To create a virtual environment, do the following: Then to enable it, then: -Windows: `venv\Scripts\activate` +Windows: `mcprep_venv_\Scripts\activate` -MacOS and Linux: `source venv\Scripts\activate` +MacOS and Linux: `source mcprep_venv_\Scripts\activate` This will make your terminal use the virtual environment until you close it or use `deactivate`. Each time you open your terminal after this, remember to enable the virtual environment From e5b843075bce5c25e7a80da714255713d1ddf12e Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:33:15 -0600 Subject: [PATCH 028/103] fixed another mistake --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f6a05af..8b3282c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,7 +192,7 @@ Then to enable it, then: Windows: `mcprep_venv_\Scripts\activate` -MacOS and Linux: `source mcprep_venv_\Scripts\activate` +MacOS and Linux: `source mcprep_venv_\bin\activate` This will make your terminal use the virtual environment until you close it or use `deactivate`. Each time you open your terminal after this, remember to enable the virtual environment From ef6caf681204be4fc3de938ab84d64829f83a991 Mon Sep 17 00:00:00 2001 From: StandingPad <75058058+StandingPadAnimations@users.noreply.github.com> Date: Sun, 1 Jan 2023 13:22:09 -0600 Subject: [PATCH 029/103] Fixed file paths for UNIX systems --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b3282c0..98ae96f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,7 +192,7 @@ Then to enable it, then: Windows: `mcprep_venv_\Scripts\activate` -MacOS and Linux: `source mcprep_venv_\bin\activate` +MacOS and Linux: `source mcprep_venv_/bin/activate` This will make your terminal use the virtual environment until you close it or use `deactivate`. Each time you open your terminal after this, remember to enable the virtual environment From 3990a795e9043433a957509eda3d2e8f5471a367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20Ph=E1=BA=A1m?= <0znightlord0@gmail.com> Date: Fri, 13 Jan 2023 15:22:53 +0700 Subject: [PATCH 030/103] update create_node,normal map using wrong interpolation create_node add functionality for nodegroup name string --- MCprep_addon/materials/generate.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 6fd00973..9703e97a 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -964,6 +964,7 @@ def create_node(tree_nodes, node_type, **attrs): tree_nodes: the material node tree's nodes node_type: the type of the node **attrs: set attributes if that node type has (eg: location, name, blend_type...) + "node_tree" can be referencing nodegroup or name of that nodegroup "hide_sockets" to hide the sockets only display linked when need """ if node_type == 'ShaderNodeMixRGB': # MixRGB in 3.4 @@ -975,8 +976,10 @@ def create_node(tree_nodes, node_type, **attrs): else: node = tree_nodes.new(node_type) for attr, value in attrs.items(): - if hasattr(node, attr): + if hasattr(node, attr) and attr != 'node_tree': setattr(node, attr, value) + elif attr == 'node_tree': #node group + setattr(node, attr, bpy.data.node_groups[value] if type(value) is str else value) elif attr == 'hide_sockets': # option to hide socket for big node node.inputs.foreach_set('hide', [value] * len(node.inputs)) node.outputs.foreach_set('hide', [value] * len(node.outputs)) @@ -1082,7 +1085,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", location = (-380, 140),interpolation = 'Closest') nodeTexNorm = create_node(nodes, "ShaderNodeTexImage", name = "Normal Texture", label = "Normal Texture", - location = (-680, -500), interpolation = 'Closest') + location = (-680, -500)) nodeTexSpec = create_node(nodes, "ShaderNodeTexImage", name = "Specular Texture", label = "Specular Texture", location = (-380, -180), interpolation = 'Closest') nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", @@ -1180,7 +1183,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", location = (-380, 140), interpolation = 'Closest') nodeTexNorm = create_node(nodes, "ShaderNodeTexImage", name = "Normal Texture", label = "Normal Texture", - location = (-680, -500), interpolation = 'Closest') + location = (-680, -500)) nodeTexSpec = create_node(nodes, "ShaderNodeTexImage", name = "Specular Texture", label = "Specular Texture", location = (-580, -180), interpolation = 'Closest') nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", @@ -1874,7 +1877,7 @@ def matgen_special_glass(mat, passes): nodeTexDiff = create_node(nodes, 'ShaderNodeTexImage', name = "Diffuse Texture", label = "Diffuse Texture", location = (-380, 140), interpolation = 'Closest', image = image_diff) nodeTexNorm = create_node(nodes, 'ShaderNodeTexImage', name = "Normal Texture", label = "Normal Texture", - location = (-680, -180), interpolation = 'Closest') + location = (-680, -180)) nodeNormalInv = create_node(nodes, 'ShaderNodeRGBCurve', name = "Normal Inverse", label = "Normal Inverse", location = (-380, -180)) nodeNormal = create_node(nodes, 'ShaderNodeNormalMap', location = (-80, -180)) nodeDiff = create_node(nodes, 'ShaderNodeBsdfDiffuse', location = (120, 0)) From 97dbbbecc17d3784142cb8e22d6ff7d20aaaba4b Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Tue, 17 Jan 2023 23:02:56 -0800 Subject: [PATCH 031/103] Updating for target style consistency. --- MCprep_addon/materials/generate.py | 426 ++++++++++++++++++++--------- 1 file changed, 291 insertions(+), 135 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 9703e97a..85966bc7 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -958,46 +958,53 @@ def set_saturation_material(mat): sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) + def create_node(tree_nodes, node_type, **attrs): """Create node with default attributes + Args: tree_nodes: the material node tree's nodes node_type: the type of the node - **attrs: set attributes if that node type has (eg: location, name, blend_type...) - "node_tree" can be referencing nodegroup or name of that nodegroup - "hide_sockets" to hide the sockets only display linked when need + **attrs: set attributes if that node type has + (eg: location, name, blend_type...) + "node_tree" can be referencing nodegroup or name of that nodegroup + "hide_sockets" to hide the sockets only display linked when need """ - if node_type == 'ShaderNodeMixRGB': # MixRGB in 3.4 + if node_type == 'ShaderNodeMixRGB': # MixRGB in 3.4 if util.min_bv((3, 4, 0)): node = tree_nodes.new('ShaderNodeMix') node.data_type = 'RGBA' else: node = tree_nodes.new('ShaderNodeMixRGB') else: - node = tree_nodes.new(node_type) + node = tree_nodes.new(node_type) for attr, value in attrs.items(): if hasattr(node, attr) and attr != 'node_tree': setattr(node, attr, value) - elif attr == 'node_tree': #node group - setattr(node, attr, bpy.data.node_groups[value] if type(value) is str else value) - elif attr == 'hide_sockets': # option to hide socket for big node + elif attr == 'node_tree': # node group + assign = bpy.data.node_groups[value] if type(value) is str else value + setattr(node, attr, assign) + elif attr == 'hide_sockets': # option to hide socket for big node node.inputs.foreach_set('hide', [value] * len(node.inputs)) node.outputs.foreach_set('hide', [value] * len(node.outputs)) return node + def get_node_socket(node, is_input=True): - """Get the input or output sockets indicies for node""" + """Gets the input or output sockets indicies for node""" n_type = node.bl_idname if n_type == 'ShaderNodeMix' or n_type == 'ShaderNodeMixRGB': - # Mix Color in 3.4 use socket indicies 0, 6, 7 for Factor, Color inputs and output with 2. - # MixRGB uses 0, 1, 2 and 0 - if util.min_bv((3, 4, 0)): - if node.data_type == 'RGBA': - inputs, outputs = [0,6,7], [2] - else: - inputs, outputs = [0,1,2], [0] - else: - inputs, outputs = [i for i in range(len(node.inputs))], [i for i in range(len(node.outputs))] + # Mix Color in 3.4 use socket indicies 0, 6, 7 for Factor, Color inputs + # and output with 2. + # MixRGB uses 0, 1, 2 and 0 + if util.min_bv((3, 4, 0)): + if node.data_type == 'RGBA': + inputs, outputs = [0, 6, 7], [2] + else: + inputs, outputs = [0, 1, 2], [0] + else: + inputs = [i for i in range(len(node.inputs))] + outputs = [i for i in range(len(node.outputs))] return inputs if is_input else outputs # ----------------------------------------------------------------------------- @@ -1082,27 +1089,54 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the necessary nodes - nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", - location = (-380, 140),interpolation = 'Closest') - nodeTexNorm = create_node(nodes, "ShaderNodeTexImage", name = "Normal Texture", label = "Normal Texture", - location = (-680, -500)) - nodeTexSpec = create_node(nodes, "ShaderNodeTexImage", name = "Specular Texture", label = "Specular Texture", - location = (-380, -180), interpolation = 'Closest') - nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", - location = (-80, 140), blend_type = 'MULTIPLY', mute = True, hide = True) - - nodeSpecInv = create_node(nodes, "ShaderNodeInvert", name = "Specular Inverse", label = "Specular Inverse", location = (-80, -280)) - nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve", name = "Normal Inverse", label = "Normal Inverse", location = (-380, -500)) - nodeNormal = create_node(nodes, "ShaderNodeNormalMap", location = (-80, -500)) + nodeTexDiff = create_node( + nodes, "ShaderNodeTexImage", + name="Diffuse Texture", + label="Diffuse Texture", + location=(-380, 140), + interpolation='Closest') + nodeTexNorm = create_node( + nodes, "ShaderNodeTexImage", + name="Normal Texture", + label="Normal Texture", + location=(-680, -500)) + nodeTexSpec = create_node( + nodes, "ShaderNodeTexImage", + name="Specular Texture", + label="Specular Texture", + location=(-380, -180), + interpolation='Closest') + nodeSaturateMix = create_node( + nodes, "ShaderNodeMixRGB", + name="Add Color", + label="Add Color", + location=(-80, 140), + blend_type='MULTIPLY', + mute=True, + hide=True) + + nodeSpecInv = create_node( + nodes, "ShaderNodeInvert", + name="Specular Inverse", + label="Specular Inverse", + location=(-80, -280)) + nodeNormalInv = create_node( + nodes, "ShaderNodeRGBCurve", + name="Normal Inverse", + label="Normal Inverse", + location=(-380, -500)) + nodeNormal = create_node( + nodes, "ShaderNodeNormalMap", + location=(-80, -500)) # Sets values - nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending + nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) # Get MixRGB sockets - saturateMixIn = get_node_socket(nodeSaturateMix) #socket inputs - saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) #socket outputs + saturateMixIn = get_node_socket(nodeSaturateMix) + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # Links the nodes to the reroute nodes. links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) @@ -1180,28 +1214,59 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): image_spec = passes["specular"] # Creates the necessary nodes - nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", - location = (-380, 140), interpolation = 'Closest') - nodeTexNorm = create_node(nodes, "ShaderNodeTexImage", name = "Normal Texture", label = "Normal Texture", - location = (-680, -500)) - nodeTexSpec = create_node(nodes, "ShaderNodeTexImage", name = "Specular Texture", label = "Specular Texture", - location = (-580, -180), interpolation = 'Closest') - nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", - location = (-80, 140), blend_type = 'MULTIPLY', mute = True, hide = True) - - nodeSpecInv = create_node(nodes, "ShaderNodeInvert", name = "Smooth Inverse", label = "Smooth Inverse", location = (-80, -280)) - nodeSeperate = create_node(nodes, "ShaderNodeSeparateRGB", name = "RGB Seperation", label = "RGB Seperation", location = (-280, -280)) - nodeNormal = create_node(nodes, "ShaderNodeNormalMap", location = (-80, -500)) - nodeNormalInv = create_node(nodes, "ShaderNodeRGBCurve", name = "Normal Inverse", label = "Normal Inverse", location = (-380, -500)) + nodeTexDiff = create_node( + nodes, "ShaderNodeTexImage", + name="Diffuse Texture", + label="Diffuse Texture", + location=(-380, 140), + interpolation='Closest') + nodeTexNorm = create_node( + nodes, "ShaderNodeTexImage", + name="Normal Texture", + label="Normal Texture", + location=(-680, -500)) + nodeTexSpec = create_node( + nodes, "ShaderNodeTexImage", + name="Specular Texture", + label="Specular Texture", + location=(-580, -180), + interpolation='Closest') + nodeSaturateMix = create_node( + nodes, "ShaderNodeMixRGB", + name="Add Color", + label="Add Color", + location=(-80, 140), + blend_type='MULTIPLY', + mute=True, + hide=True) + + nodeSpecInv = create_node( + nodes, "ShaderNodeInvert", + name="Smooth Inverse", + label="Smooth Inverse", + location=(-80, -280)) + nodeSeperate = create_node( + nodes, "ShaderNodeSeparateRGB", + name="RGB Seperation", + label="RGB Seperation", + location=(-280, -280)) + nodeNormal = create_node( + nodes, "ShaderNodeNormalMap", + location=(-80, -500)) + nodeNormalInv = create_node( + nodes, "ShaderNodeRGBCurve", + name="Normal Inverse", + label="Normal Inverse", + location=(-380, -500)) # Sets values - nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending + nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) # Get MixRGB sockets - saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs - saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs + saturateMixIn = get_node_socket(nodeSaturateMix) + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # Links the nodes to the reroute nodes. links.new(nodeTexDiff.outputs["Color"], nodeSaturateMix.inputs[saturateMixIn[1]]) @@ -1256,7 +1321,8 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): else: conf.log("Texture desaturated: " + canon, vv_only=True) desat_color = conf.json_data['blocks']['desaturated'][canon] - if len(desat_color) < len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value): + desat_cmpr = len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value) + if len(desat_color) < desat_cmpr: desat_color.append(1.0) nodeSaturateMix.inputs[saturateMixIn[2]].default_value = desat_color nodeSaturateMix.mute = False @@ -1301,13 +1367,24 @@ def matgen_cycles_simple( links = mat.node_tree.links nodes.clear() - nodeTexDiff = create_node(nodes, "ShaderNodeTexImage", name = "Diffuse Texture", label = "Diffuse Texture", - location = (0, 0), interpolation = 'Closest', image = image_diff) - nodeSaturateMix = create_node(nodes, "ShaderNodeMixRGB", name = "Add Color", label = "Add Color", - location = (300, 0), blend_type = 'MULTIPLY', mute = True, hide = True) - - principled = create_node(nodes, "ShaderNodeBsdfPrincipled", location = (600, 0)) - node_out = create_node(nodes, "ShaderNodeOutputMaterial", location = (900, 0)) + nodeTexDiff = create_node( + nodes, "ShaderNodeTexImage", + name="Diffuse Texture", + label="Diffuse Texture", + location=(0, 0), + interpolation='Closest', + image=image_diff) + nodeSaturateMix = create_node( + nodes, "ShaderNodeMixRGB", + name="Add Color", + label="Add Color", + location=(300, 0), + blend_type='MULTIPLY', + mute=True, + hide=True) + + principled = create_node(nodes, "ShaderNodeBsdfPrincipled", location=(600, 0)) + node_out = create_node(nodes, "ShaderNodeOutputMaterial", location=(900, 0)) # Sets default reflective values if use_reflections and checklist(canon, "reflective"): @@ -1324,10 +1401,11 @@ def matgen_cycles_simple( principled.inputs["Metallic"].default_value = 0 # Get MixRGB sockets - saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs - saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs + saturateMixIn = get_node_socket(nodeSaturateMix) + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) - # Set values | Specular tends to cause some issues with how blocks look, so let's disable it + # Set values. + # Specular causes issues with how blocks look, so let's disable it. nodeSaturateMix.inputs[saturateMixIn[0]].default_value = 1.0 principled.inputs["Specular"].default_value = 0 @@ -1354,7 +1432,9 @@ def matgen_cycles_simple( inputs = [inp.name for inp in principled.inputs] if 'Emission Strength' in inputs: # Later 2.9 versions only. principled.inputs['Emission Strength'].default_value = 1 - links.new(nodeSaturateMix.outputs[saturateMixOut[0]], principled.inputs["Emission"]) + links.new( + nodeSaturateMix.outputs[saturateMixOut[0]], + principled.inputs["Emission"]) # reapply animation data if any to generated nodes apply_texture_animation_pass_settings(mat, animated_data) @@ -1367,7 +1447,8 @@ def matgen_cycles_simple( else: conf.log("Texture desaturated: " + canon, vv_only=True) desat_color = conf.json_data['blocks']['desaturated'][canon] - if len(desat_color) < len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value): + desat_cmpr = len(nodeSaturateMix.inputs[saturateMixIn[2]].default_value) + if len(desat_color) < desat_cmpr: desat_color.append(1.0) nodeSaturateMix.inputs[saturateMixIn[2]].default_value = desat_color nodeSaturateMix.mute = False @@ -1408,17 +1489,26 @@ def matgen_cycles_principled( links = mat.node_tree.links nodes.clear() - principled = create_node(nodes, "ShaderNodeBsdfPrincipled", location = (120, 0)) - nodeEmit = create_node(nodes, "ShaderNodeEmission", location = (120, 140)) - nodeEmitCam = create_node(nodes, "ShaderNodeEmission", location = (120, 260)) - nodeMixCam = create_node(nodes, "ShaderNodeMixShader", location = (320, 260)) - nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff", location = (-80, 320)) - nodeLightPath = create_node(nodes, "ShaderNodeLightPath", location = (-320, 520)) - nodeMixEmit = create_node(nodes, "ShaderNodeMixShader", location = (420, 0)) - nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent", location = (420, 140)) - nodeMixTrans = create_node(nodes, "ShaderNodeMixShader", location = (620, 0)) - nodeOut = create_node(nodes, "ShaderNodeOutputMaterial", location = (820, 0)) - + principled = create_node( + nodes, "ShaderNodeBsdfPrincipled", location=(120, 0)) + nodeEmit = create_node( + nodes, "ShaderNodeEmission", location=(120, 140)) + nodeEmitCam = create_node( + nodes, "ShaderNodeEmission", location=(120, 260)) + nodeMixCam = create_node( + nodes, "ShaderNodeMixShader", location=(320, 260)) + nodeFalloff = create_node( + nodes, "ShaderNodeLightFalloff", location=(-80, 320)) + nodeLightPath = create_node( + nodes, "ShaderNodeLightPath", location=(-320, 520)) + nodeMixEmit = create_node( + nodes, "ShaderNodeMixShader", location=(420, 0)) + nodeTrans = create_node( + nodes, "ShaderNodeBsdfTransparent", location=(420, 140)) + nodeMixTrans = create_node( + nodes, "ShaderNodeMixShader", location=(620, 0)) + nodeOut = create_node( + nodes, "ShaderNodeOutputMaterial", location=(820, 0)) # Sets default transparency value nodeMixTrans.inputs[0].default_value = 1 @@ -1542,44 +1632,65 @@ def matgen_cycles_original( nodes.clear() # Shader - nodeDiff = create_node(nodes, "ShaderNodeBsdfDiffuse", location = (940, 200)) - nodeGlossDiff = create_node(nodes, "ShaderNodeBsdfGlossy", location = (940, 60)) - nodeGlossMetallic = create_node(nodes, "ShaderNodeBsdfGlossy", location = (1140, -120)) - nodeTrans = create_node(nodes, "ShaderNodeBsdfTransparent", location = (1740, 120)) - nodeEmit = create_node(nodes, "ShaderNodeEmission", location = (1340, 120)) - nodeEmitCam = create_node(nodes, "ShaderNodeEmission", location = (1340, 240)) + nodeDiff = create_node(nodes, "ShaderNodeBsdfDiffuse", location=(940, 200)) + nodeGlossDiff = create_node(nodes, "ShaderNodeBsdfGlossy", location=(940, 60)) + nodeGlossMetallic = create_node( + nodes, "ShaderNodeBsdfGlossy", location=(1140, -120)) + nodeTrans = create_node( + nodes, "ShaderNodeBsdfTransparent", location=(1740, 120)) + nodeEmit = create_node(nodes, "ShaderNodeEmission", location=(1340, 120)) + nodeEmitCam = create_node(nodes, "ShaderNodeEmission", location=(1340, 240)) # Mix Shader - nodeMixDiff = create_node(nodes, "ShaderNodeMixShader", location = (1140, 40)) - nodeMixMetallic = create_node(nodes, "ShaderNodeMixShader", location = (1340, 0)) - nodeMixTrans = create_node(nodes, "ShaderNodeMixShader", location = (1940, 0)) - nodeMixEmit = create_node(nodes, "ShaderNodeMixShader", location = (1740, 0)) - nodeMixCam = create_node(nodes, "ShaderNodeMixShader", location = (1540, 240)) - # Mix - nodeMixRGBDiff = create_node(nodes, "ShaderNodeMixRGB", location = (560, 200)) - nodeMixRGB = create_node(nodes, "ShaderNodeMixRGB", location = (180, 360)) - nodeMixRGBMetallic = create_node(nodes, "ShaderNodeMixRGB", location = (940, -120)) + nodeMixDiff = create_node(nodes, "ShaderNodeMixShader", location=(1140, 40)) + nodeMixMetallic = create_node( + nodes, "ShaderNodeMixShader", location=(1340, 0)) + nodeMixTrans = create_node(nodes, "ShaderNodeMixShader", location=(1940, 0)) + nodeMixEmit = create_node(nodes, "ShaderNodeMixShader", location=(1740, 0)) + nodeMixCam = create_node(nodes, "ShaderNodeMixShader", location=(1540, 240)) + # Mix + nodeMixRGBDiff = create_node(nodes, "ShaderNodeMixRGB", location=(560, 200)) + nodeMixRGB = create_node(nodes, "ShaderNodeMixRGB", location=(180, 360)) + nodeMixRGBMetallic = create_node( + nodes, "ShaderNodeMixRGB", location=(940, -120)) # Input - nodeFresnel = create_node(nodes, "ShaderNodeFresnel", location = (360, 160)) - nodeFresnelMetallic = create_node(nodes, "ShaderNodeFresnel", location = (740, -120)) - nodeLightPath = create_node(nodes, "ShaderNodeLightPath", location = (1340, 600)) - nodeGeometry = create_node(nodes, "ShaderNodeNewGeometry", location = (0, 600)) + nodeFresnel = create_node(nodes, "ShaderNodeFresnel", location=(360, 160)) + nodeFresnelMetallic = create_node( + nodes, "ShaderNodeFresnel", location=(740, -120)) + nodeLightPath = create_node( + nodes, "ShaderNodeLightPath", location=(1340, 600)) + nodeGeometry = create_node(nodes, "ShaderNodeNewGeometry", location=(0, 600)) # Math - nodeMathPower = create_node(nodes, "ShaderNodeMath", location = (0, 360), operation = "POWER") - nodeMathPowerDiff = create_node(nodes, "ShaderNodeMath", location = (360, 360), operation = "POWER") - nodeMathMultiplyDiff = create_node(nodes, "ShaderNodeMath", location = (740, 200), operation = "MULTIPLY") - nodeMathMetallic = create_node(nodes, "ShaderNodeMath", location = (740, -280), operation = "MULTIPLY") + nodeMathPower = create_node( + nodes, "ShaderNodeMath", + location=(0, 360), + operation="POWER") + nodeMathPowerDiff = create_node( + nodes, "ShaderNodeMath", + location=(360, 360), + operation="POWER") + nodeMathMultiplyDiff = create_node( + nodes, "ShaderNodeMath", + location=(740, 200), + operation="MULTIPLY") + nodeMathMetallic = create_node( + nodes, "ShaderNodeMath", + location=(740, -280), + operation="MULTIPLY") # Etc - nodeBump = create_node(nodes, "ShaderNodeBump", location = (-200, 600)) - nodeFalloff = create_node(nodes, "ShaderNodeLightFalloff", location = (1140, 240)) - nodeOut = create_node(nodes, "ShaderNodeOutputMaterial", location = (2140, 0)) + nodeBump = create_node( + nodes, "ShaderNodeBump", location=(-200, 600)) + nodeFalloff = create_node( + nodes, "ShaderNodeLightFalloff", location=(1140, 240)) + nodeOut = create_node( + nodes, "ShaderNodeOutputMaterial", location=(2140, 0)) # Get MixRGB sockets - mixDiffIn = get_node_socket(nodeMixRGBDiff) #socket inputs - mixIn = get_node_socket(nodeMixRGB) + mixDiffIn = get_node_socket(nodeMixRGBDiff) + mixIn = get_node_socket(nodeMixRGB) mixMetallicIn = get_node_socket(nodeMixRGBMetallic) - mixOut = get_node_socket(nodeMixRGB, is_input=False) #socket outputs + mixOut = get_node_socket(nodeMixRGB, is_input=False) mixDiffOut = get_node_socket(nodeMixRGBDiff, is_input=False) - mixMetallicOut = get_node_socket(nodeMixRGBMetallic, is_input=False) + mixMetallicOut = get_node_socket(nodeMixRGBMetallic, is_input=False) # Sets default transparency value nodeMixTrans.inputs["Fac"].default_value = 1 @@ -1624,17 +1735,24 @@ def matgen_cycles_original( links.new(nodeFresnel.outputs[0], nodeMixRGBDiff.inputs[mixDiffIn[0]]) links.new(nodeGeometry.outputs["Incoming"], nodeMixRGB.inputs[mixIn[2]]) links.new(nodeBump.outputs["Normal"], nodeMixRGB.inputs[mixIn[1]]) - links.new(nodeMathPowerDiff.outputs["Value"], nodeMixRGBDiff.inputs[mixDiffIn[1]]) - links.new(nodeMixRGBDiff.outputs[mixDiffOut[0]], nodeMathMultiplyDiff.inputs[0]) + links.new( + nodeMathPowerDiff.outputs["Value"], + nodeMixRGBDiff.inputs[mixDiffIn[1]]) + links.new( + nodeMixRGBDiff.outputs[mixDiffOut[0]], + nodeMathMultiplyDiff.inputs[0]) links.new(nodeMathMultiplyDiff.outputs["Value"], nodeMixDiff.inputs["Fac"]) links.new(nodeDiff.outputs["BSDF"], nodeMixDiff.inputs[1]) links.new(nodeGlossDiff.outputs["BSDF"], nodeMixDiff.inputs[2]) links.new( - nodeFresnelMetallic.outputs["Fac"], nodeMixRGBMetallic.inputs[mixMetallicIn[0]]) + nodeFresnelMetallic.outputs["Fac"], + nodeMixRGBMetallic.inputs[mixMetallicIn[0]]) links.new( - nodeMathMetallic.outputs["Value"], nodeMixRGBMetallic.inputs[mixMetallicIn[2]]) + nodeMathMetallic.outputs["Value"], + nodeMixRGBMetallic.inputs[mixMetallicIn[2]]) links.new( - nodeMixRGBMetallic.outputs[mixMetallicOut[0]], nodeGlossMetallic.inputs["Color"]) + nodeMixRGBMetallic.outputs[mixMetallicOut[0]], + nodeGlossMetallic.inputs["Color"]) links.new(nodeGlossMetallic.outputs["BSDF"], nodeMixMetallic.inputs[2]) links.new(nodeMixMetallic.outputs["Shader"], nodeMixEmit.inputs[1]) links.new(nodeMixCam.outputs["Shader"], nodeMixEmit.inputs[2]) @@ -1745,24 +1863,49 @@ def matgen_special_water(mat, passes): links = mat.node_tree.links nodes.clear() - nodeTexDiff = create_node(nodes,'ShaderNodeTexImage', name = "Diffuse Texure", label = "Diffuse Texure", - location = (-180, 140), interpolation = 'Closest', image = image_diff) - nodeTexNorm = create_node(nodes,'ShaderNodeTexImage',name = "Normal Texure",label = "Normal Texure", - location = (-290, -180), interpolation = 'Closest') - nodeSaturateMix = create_node(nodes,'ShaderNodeMixRGB', name = "Add Color", label = "Add Color", - location = (320, 140), blend_type = 'MULTIPLY', mute = True, hide = True) - nodeNormalInv = create_node(nodes,'ShaderNodeRGBCurve',name = "Normal Inverse", label = "Normal Inverse", location = (10, -180)) - nodeNormal = create_node(nodes,'ShaderNodeNormalMap', location = (310, -180)) - nodeBrightContrast = create_node(nodes,'ShaderNodeBrightContrast', location = (120, 140)) - - nodeGlass = create_node(nodes,'ShaderNodeBsdfGlass', location = (520, 140)) - nodeTrans = create_node(nodes,'ShaderNodeBsdfTransparent', location = (520, 340)) - nodeMixTrans = create_node(nodes,'ShaderNodeMixShader', location = (720, 140)) - nodeOut = create_node(nodes,'ShaderNodeOutputMaterial', location = (920, 140)) + nodeTexDiff = create_node( + nodes, 'ShaderNodeTexImage', + name="Diffuse Texure", + label="Diffuse Texure", + location=(-180, 140), + interpolation='Closest', + image=image_diff) + nodeTexNorm = create_node( + nodes, 'ShaderNodeTexImage', + name="Normal Texure", + label="Normal Texure", + location=(-290, -180), + interpolation='Closest') + nodeSaturateMix = create_node( + nodes, 'ShaderNodeMixRGB', + name="Add Color", + label="Add Color", + location=(320, 140), + blend_type='MULTIPLY', + mute=True, + hide=True) + nodeNormalInv = create_node( + nodes, 'ShaderNodeRGBCurve', + name="Normal Inverse", + label="Normal Inverse", + location=(10, -180)) + nodeNormal = create_node( + nodes, 'ShaderNodeNormalMap', location=(310, -180)) + nodeBrightContrast = create_node( + nodes, 'ShaderNodeBrightContrast', location=(120, 140)) + + nodeGlass = create_node( + nodes, 'ShaderNodeBsdfGlass', location=(520, 140)) + nodeTrans = create_node( + nodes, 'ShaderNodeBsdfTransparent', location=(520, 340)) + nodeMixTrans = create_node( + nodes, 'ShaderNodeMixShader', location=(720, 140)) + nodeOut = create_node( + nodes, 'ShaderNodeOutputMaterial', location=(920, 140)) # Mix RGB sockets for 3.4 - saturateMixIn = get_node_socket(nodeSaturateMix) # socket inputs - saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # socket outputs + saturateMixIn = get_node_socket(nodeSaturateMix) + saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # Sets default values nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending @@ -1874,17 +2017,30 @@ def matgen_special_glass(mat, passes): links = mat.node_tree.links nodes.clear() - nodeTexDiff = create_node(nodes, 'ShaderNodeTexImage', name = "Diffuse Texture", label = "Diffuse Texture", - location = (-380, 140), interpolation = 'Closest', image = image_diff) - nodeTexNorm = create_node(nodes, 'ShaderNodeTexImage', name = "Normal Texture", label = "Normal Texture", - location = (-680, -180)) - nodeNormalInv = create_node(nodes, 'ShaderNodeRGBCurve', name = "Normal Inverse", label = "Normal Inverse", location = (-380, -180)) - nodeNormal = create_node(nodes, 'ShaderNodeNormalMap', location = (-80, -180)) - nodeDiff = create_node(nodes, 'ShaderNodeBsdfDiffuse', location = (120, 0)) - nodeMixTrans = create_node(nodes, 'ShaderNodeMixShader', location = (620, 0)) - nodeGlass = create_node(nodes, 'ShaderNodeBsdfGlass', location = (120, 240)) - nodeBrightContrast = create_node(nodes, 'ShaderNodeBrightContrast', location = (420, 0)) - nodeOut = create_node(nodes, 'ShaderNodeOutputMaterial', location = (820, 0)) + nodeTexDiff = create_node( + nodes, 'ShaderNodeTexImage', + name="Diffuse Texture", + label="Diffuse Texture", + location=(-380, 140), + interpolation='Closest', + image=image_diff) + nodeTexNorm = create_node( + nodes, 'ShaderNodeTexImage', + name="Normal Texture", + label="Normal Texture", + location=(-680, -180)) + nodeNormalInv = create_node( + nodes, 'ShaderNodeRGBCurve', + name="Normal Inverse", + label="Normal Inverse", + location=(-380, -180)) + nodeNormal = create_node(nodes, 'ShaderNodeNormalMap', location=(-80, -180)) + nodeDiff = create_node(nodes, 'ShaderNodeBsdfDiffuse', location=(120, 0)) + nodeMixTrans = create_node(nodes, 'ShaderNodeMixShader', location=(620, 0)) + nodeGlass = create_node(nodes, 'ShaderNodeBsdfGlass', location=(120, 240)) + nodeBrightContrast = create_node( + nodes, 'ShaderNodeBrightContrast', location=(420, 0)) + nodeOut = create_node(nodes, 'ShaderNodeOutputMaterial', location=(820, 0)) # Sets default transparency value nodeMixTrans.inputs[0].default_value = 1 From 3648c930c7222c51f1455e7ebfb9317eb6292576 Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 22 Jan 2023 19:31:55 -0600 Subject: [PATCH 032/103] Added readme to explain what this is --- README.md | 587 ++---------------------------------------------------- 1 file changed, 17 insertions(+), 570 deletions(-) diff --git a/README.md b/README.md index 88b567fb..a8c3417f 100644 --- a/README.md +++ b/README.md @@ -1,578 +1,25 @@ +# MCprep Kaion -MCprep Addon -====== -[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/mb8hBUC) -[![MCprep License](https://img.shields.io/github/license/TheDuckCow/MCprep)](https://www.gnu.org/licenses/gpl-3.0) -![MCprep Stars](https://img.shields.io/github/stars/TheDuckCow/MCprep) -[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/TheDuckCow/MCprep/blob/master/CONTRIBUTING.md) +## What is it? +A fork of MCprep focused on advanced features. Most of these features have been suggested in the past but haven't been implemented due to the following: +* Finer details haven't been worked out (most common reason) +* Requires external Python libraries (which can be hard to justify the extra work during installation) +* Low priority (features that are nice and as such aren't worth implementing) +This fork mostly aims to be a proof of concept, so any features that rely on assets (like meshswap) are missing. -MCprep is an addon dedicated to speeding up the workflow of Minecraft animators in Blender by automatically fixing up materials and providing other tools such as mob spawing, effects spawning, etc. +## Is it right for me? +MCprep Kaion is not meant for normal use, I suggest using [mainline MCprep](https://github.com/TheDuckCow/MCprep). If you depend on the features that MCprep Kaion provides, it's simply best to suggest the feature on the [MCprep server](https://discord.gg/mb8hBUC) -## Installing MCprep -### Click the link below and download the .zip file (re-zip if auto-unzipped into a folder necessary), install into blender (2.78 through 3.2 supported) +## Aren't you one of the MCprep maintainers? Why does this exist? +To answer the first question, yes I am, though I don't contribute much to MCprep anymore, see [here](https://standingpadanimations.github.io/posts/goodbye-for-now/) for more. The gist is that I left the Minecraft rendering community due to some drama, and in the process moved away from MCprep as well. -[![Install MCprep](/visuals/mcprep_download.png)](https://theduckcow.com/dev/blender/mcprep-download/) +The idea for a fork came when I made the optimizer. I originally wanted to make a version of MCprep with far more power (yet destructive) optimizations. Eventually I also wanted to add features that in hindsight would take a while before implementation details were finalized. -*By downloading and installing, you agree to the following [privacy policy](https://theduckcow.com/privacy-policy).* **[Watch this video](https://www.youtube.com/watch?v=i6Ne07-eIyI) on how to install if running into troubles.** +After I left Minecraft rendering, I wanted to bring some of those features to MCprep, and created this fork. +## Will some of these features be merged eventually? +Since MCprep Kaion is best to be treated as a proof of concept fork for ideas, the hope is that most (if not all) of the changes made here will be merged upstream. -It should remain a zip folder. In blender, go to preferences, then the addons tab, and at the bottom of the window install from file. Select the .zip file. **NOTE:** _Blender may not automatically enable the addon. If the MCprep addon is not already shown in the window after installing, search for it at left and then ensure the check box is enabled._ **Save user preferences** to keep it enabled next time blender opens. - -*Again, please download from the link above or the releases page, __not__ by clicking `download zip` button above.* - -*The preferences panel should look like this after installing the zip file* -![Installed MCprep 2.7](/visuals/install.png?raw=true) - -*Or, if using Blender 2.8, follow these similar steps* -![Installed MCprep 2.8](/visuals/install_28.png?raw=true) - -**If you like the addon, [please consider donating](http://bit.ly/donate2TheDuckCow) for the continued quality development! [Share this addon](https://twitter.com/intent/tweet?text=Make+easier+Minecraft+renders+using+the+MCprep+addon+bit.ly/MCprep+by+@TheDuckCow) so others can benefit from it! Or help by [taking this quick survey](http://bit.ly/MCprepSurvey)!** - - -Learn how to use MCprep -====== - -*[Short, 1-minute videos showcasing how to use Blender+MCprep features](https://bit.ly/MCprepTutorials)* - -[![Tutorial playlist](https://img.youtube.com/vi/62fVXVAO13A/0.jpg)](https://bit.ly/MCprepTutorials) - -*[Additional Playlist of MCprep videos/demos](https://www.youtube.com/playlist?list=PL8X_CzUEVBfaajwyguIj_utPXO4tEOr7a)* - - -About MCprep -====== - -This is a blender python addon to improve and automate many aspects of creating Minecraft renders and animations. It can help you improt world's exported from Minecraft, set up better materials, importing mobs and items, and set up proxy characters for animation, and even includes default animations for common blocks/mobs like tall grass, torches, and mobs like the bat or blaze. This addon assumes you have already exported a Minecraft world to an OBJ file. While the script should work for any world importer, it has been tested and developed based on the jmc2obj and Mineways tools for exporting Minecraft worlds to obj files. - -This addon is made to work with an asset library directory, from which models and groups are linked or imported from. This library blend file is included, but does not have all types of blocks generated yet. This will be improved in the future. - -This addon is compatible officially down to 2.72 official builds, and up to blender 3.00. Not all features are available in all versions, try to use the latest available blender. *Run into any problems? [Submit bugs/issues here](https://github.com/TheDuckCow/MCprep/issues).* - - -Feature list -====== - -| World Imports | Description | -|----------|:-------------:| -| Prep Materials | Improves materials from world imports, and allows one-click switching from cycles & blender internal materials. *Note, this does not* create *materials, only modifies existing ones.* | -| Swap Texture Pack | *Initial support only for jmc2obj world exports.* Using a valid Minecraft resource pack, you can now completely replace the textures of the imported world with another pack - you can even changed individual blocks at a time. | -| Animate textures | *Initial support only for jmc2obj world exports.* With a valid (or the MCprep default) resource pack selected, you can replace still images with their animated versions. Works great to put motion back into lava, water, portals and other blocks for any kind of resource pack. | -| Combine materials/images | Consolidates duplicate materials and images down to the smallest number of unique datablocks. *Note: combine images is only available on blender 2.78+* | -| Improve UI | A shortcut to quickly improve viewport settings for Minecraft sets. Sets textured solid mode & turns off mipmaps | -| Mesh Swap | Allows you to replace simple models from 3D exported worlds with more intricate 3D models | -| Scale UV Faces | Allows you to scale all UV faces of a mesh about their origin. Must be in edit mode of a mesh. | -| Select Alpha Faces | Allows you to select or delete mesh faces that are transparent in the applied image texture. Must be in edit mode of a mesh. | -| Sync Materials | Search for the `materials.blend` file within the active texture pack, and for any matching materials found in this file, import and overwrite the current file's version of that same material. Found under the World Exporter > Advanced section or in prep materials popup. | - - -| World Tools | Description | -|----------|:-------------:| -| Create MC World | Operator which allows user to create either a static or dynamic sky. Options exist to also add clouds, remove existing sun lamps, and define how the Sun/Moon is imported (either as a shader, mesh, or neither). If a dynamic sky is created, a time-control slider will also be displayed below. | -| Prep World | This operator makes several updates to either Blender Internal, Cycles, or Eevee scenes that are optimal and in most cases will notably improve render times (particularly for cycles) and increase overall aesthetic. | - - -| Skin Swapper | Description | -|----------|:-------------:| -| Apply [skin] | Applies the currently list-selected skin to the actively selected rigs. | -| Skin from file | Loads in a skin texture from file and applies to the actively selected rigs. *Note: Also gets added to skin list* | -| Skin from username | Downloads a skin based on a play name and applies to the actively selected rigs. *Note: Also gets added to skin list* | -| + sign | Install a custom skin into the list without applying to a rig | - -*Note: only 1.8 skin layouts are supported at this time, in the future an auto-conversion feature will likely be implemented* - - -| Spawner | Description | -|----------|:-------------:| -| Spawn: [rig] | Based on the actively selected rig, from an according spawning rig category, add the mob/character into the scene. These are fully rigged characters. Using the plus sign, you can even install your own rigs (rig must be part of a group). For quicker mapping, you can even set an entire folder and it will auto-create subcategories of rigs based on folders. | -| Spawn: meshswap block | Place a block from the meshswap file into the 3D scene. Currently is limited to only having meshswap groups (e.g. torches and fire) and not objects (e.g. not supporting grass and flowers yet).| -| Spawn: items | Convert any image icon into a 3D mesh, with faces per each pixel and transparent faces already removed. Defaults to loading Minecraft items.| - - - -| Material Library | Description | -|----------|:-------------:| -| Load material | Generate the selected material and apply it to the selected material slot of the active object. Follows convention of the active resource pack and general Prep Materials operator. | - - -### Spawner mobs included in the current version of MCprep -![included-mobs](/visuals/MCprep_mobs_included.png?raw=true) - - -CREDIT -====== -While this addon is released as open source software, the assets are being released as [Creative Commons Attributions, CC-BY](https://creativecommons.org/licenses/by/3.0/us/). If you use MeshSwap, **please credit the creators** by linking to this page wherever your project may appear: [http://github.com/TheDuckCow/MCprep](https://github.com/TheDuckCow/MCprep) - -Meshswap Block models developed by [Patrick W. Crawford](https://twitter.com/TheDuckCow), [SilverC16](http://youtube.com/user/silverC16), and [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse). - - -| Player Rigs | Creator | -|----------|:-------------:| -| Fancy Feet Player | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) ([Rig link](http://bit.ly/MinecraftRig)) | -| Fancy Feet Slim Player | Modified by [Jeremy Putnam](http://www.blendswap.com/user/lorddon) (MCprep exclusive) | -| Fancy Feet Armor Player | Modified by [zNightlord](https://twitter.com/znightTrung) (MCprep exclusive) | -| Simple Player (modified) | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Simple Slim Player (modified) | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) + TheDuckCow (MCprep exclusive) | -| Shapekey Player | Derived from [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home), shapekeys and animation by [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) (MCprep exclusive) | -| Story Mode Rig | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | -| VMcomix Steve (rounded) | [VMcomix](https://www.youtube.com/user/VMComix) ([Rig link](http://vmcomix.blogspot.com/)) | - - -| Passive Rigs | Creator | -|----------|:-------------:| -| Allay | [zNightlord](https://twitter.com/znightTrung) | -| Axolotl | [Breadcrumb](https://www.youtube.com/channel/UCT1_5td4SWg67fhElGz1S4A) | -| Bat | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) (MCprep exclusive) | -| Cat | [TRPHB Animation](https://youtube.com/c/TRPHBAnimation) | -| Cod fish | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Chicken | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | -| Cow | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | -| Dolphin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Fox | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Frog | [zNightlord](https://twitter.com/znightTrung) | -| Glow squid | [TRPHB Animation](https://youtube.com/c/TRPHBAnimation) | -| Goat | [TRPHB Animation](https://youtube.com/c/TRPHBAnimation) | -| Mooshroom (red/brown) | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Parrot | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Pig | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | -| Panda | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Pufferfish| [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Rabbit | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | -| Salmon | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Sheep | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | -| Squid | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | -| Tropical Fish | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Cod fish | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Turtle | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | - - -| Hostile Rigs | Creator | -|----------|:-------------:| -| Bee | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Blaze | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | -| Cave Spider | [Austin Prescott](https://youtu.be/0xA4-yMr2KY) | -| Creeper | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) (MCprep exclusive) | -| Drowned | [thefunnypie2 + HissingCreeper](https://github.com/TheDuckCow/MCprep/issues/160#issuecomment-931923101) | -| Eldar Guardian | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Ender Dragon | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Enderman | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79750)) | -| Endermite | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | -| Evoker | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Ghast | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | -| Guardian | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79729)) | -| Hoglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Husk | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Magma Cube | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Piglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Pillager | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Polar bear | [PixelFrosty](https://twitter.com/Pixel_Frosty) ([Demo/download](https://www.youtube.com/watch?v=kbt5HWjafBk))| -| Phantom | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Ravager | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Shulker | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79729)) | -| Silverfish | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | -| Skeleton | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link, outdated](http://www.blendswap.com/blends/view/79495)) | -| Slime | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Spider | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | -| Stray | [thefunnypie2 + Trainguy9512](https://github.com/TheDuckCow/MCprep/issues/160#issuecomment-931923101) | -| Strider | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Vindicator | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Warden | [DigDanAnimates](https://twitter.com/DigDanAnimates) | -| Witch | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Wither | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Wither Skeleton | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Vindicator | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Zoglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Zombie | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | -| Zombie Pigman | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Zombie Villager | [thefunnypie2 + HissingCreeper](https://github.com/TheDuckCow/MCprep/issues/160#issuecomment-931923101) | -| Zombified Piglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | - - - -| Friendly/Utility Rigs | Creator | -|----------|:-------------:| -| Horse | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) ([Rig link](http://www.blendswap.com/blends/view/73064)) | -| Iron Golem | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79455)) | -| Llama | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Ocelot | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | -| Simple Villager (Dynamic skin) | [Th3pooka](https://twitter.com/Th3Pooka) | -| Snow Golem | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | -| Villager | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Wandering Trader | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Wolf | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link, outdated](http://www.blendswap.com/blends/view/79628)) | - - -| Entities | Creator | -|----------|:-------------:| -| Arrow | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | -| Boat | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | -| Bow | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | -| Crossbow | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | -| Minecart | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| Shield | [Ageofherosmedia](https://github.com/TheDuckCow/MCprep/issues/245#issuecomment-998745223) | -| Spyglass | [Banana-Blu](https://github.com/Banana-Blu) | - - -| Effects | Creator | -|----------|:-------------:| -| Geo node effects | [Th3pooka](https://twitter.com/Th3Pooka) | -| TNT effects (both) | [TheDuckCow](http://www.youtube.com/TheDuckCow) | -| Rain + Snow particles | [TheDuckCow](http://www.youtube.com/TheDuckCow) | - - -| Meshswap Blocks | Creator | -|----------|:-------------:| -| Campfire | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | -| All others | [TheDuckCow](http://www.youtube.com/TheDuckCow) | - - -If you use any of these rigs in your animation, give credit to the according creator or by directly referring back to this readme file which contains all the credits. *Models have been slightly modified to function best and consistently with the MCprep addon.* - - -Thank you to all the contributors to this project! It will continue to grow and cover more assets in the near future. - - - -How to use this addon -====== - - -### Prep materials: -- **Purpose:** To automatically setup better, crisp materials for rendering Minecraft, low resolution textures. It works for both Blender internal as well as cycles, and more recently the cycles Principled shader. It will even selectively turn 'shiny' materials into reflecting materials, as well as 'bright' materials into emitting materials. Currently, the list of these materials is hard-coded into a json file, with names matching the material names for both jmc2obj and Mineways. You can modify this file for your own needs, but largely should be fine with the default, and will be updated (overwritten) as new updates are added. -- **Step 1:** Export your world to an OBJ or other general 3D formats. I use jmc2obj, but Mineways or other such formats should be fine as well. -- **Step 2:** Import the world into blender (e.g. via file > import > obj, or whatever according format) -- **Step 3:** Select all, or select the objects that have the material you want to fix. Materials can be all separate objects or the same object, it does not matter. -- **Step 4:** Under the MCprep panel, press "Prep Materials". -- **Step 5:** Change settings (optional, defaults are good!) and press ok - and wait a moment while the addon does the rest! Settings - -*Settings popup for prep materials.* - -![Prep material settings](/visuals/prepMaterialsSettings.png?raw=true) - -Setting options: - - **Use principled shader:** Cycles only and 2.79 and up, use the more physically accurate Principled shader node. If disabled, it will default back to previous material setups. The principled shader is, however, preferred. - - **Use reflections:** Add extra settings to make material more reflective. For Blender Internal, enable reflections. For cycles materials, increase reflection alpha so that even fully transparent sections of image (e.g. in glass) will show some reflections. - - **Animate textures:** See the section below, this performs the same operation after prepping materials, with the default behavior of "save to next to current source image", which is likely in the texture pack itself - - **Find missing images:** Look to replace any missing images with their counterparts in the active resource pack, which by default is vanilla Minecraft textures as ship with MCprep - - **Use extra maps:** If extra passes like normal and specular maps are available with the selected resource pack, load them into the material (note: the default MCprep texture pack will not have these passes) - - **Improve UI:** Does not impact existing materials, but just sets the viewport to a nice minimum default to see the result of prepping materials - but always checked rendered mode to see how they truly look in the end - - **Combine materials:** This will go through the entire blend file and attempt to merge together same-named materials (e.g. grass and grass.001 and grass.002 will all be consolidated down into one shared material, though something like grass-edit would be left alone) - - **Sync materials:** Look for any matching material names (after casting to Minecraft canonical names) in the materials.blend file (if presnet) under the active resource pack. If found, replace this local material with that from the materials.blend file. - - -### Swap texture pack -- **Purpose:** Replace the images in a blend file with those from another Minecraft resource pack. Operates on selected objects and their respective materials, so you can mix and match different resource packs on a per-material basis this way. -- **Step 1:** Select all materials you wish to swap texture packs for -- **Step 2:** Prep materials on these objects if you haven't already (prepping a second time will do no harm); not doing this could have mixed results. -- **Step 3:** Press on the swap texture pack button in the 3D view > MCprep tab > World Imports panel -- **Step 4:** In this popup, the folder will default to the MCprep resource pack (ie default Vanilla Minecraft); navigate to an *extracted* zip folder of a valid resource pack. You can select any sub-folder, but to be safe, select the folder level such that the the "pack.png" and "assets" sub-folder are visible as rows. -- **Step 5:** Decide if you want to enable/disable pulling in extra passes (e.g. normal and specular, if they exist) and whether to animate textures upon swapping. Both of these tickboxes, on by default, are in the left-hand sidebar. - -Using Mineways? Use the following world export settings to use this feature: -![Mineways exporter settings](/visuals/mineways-settings.png?raw=true) - -Note: Use tiles for exporting to create individual files per block instead of one combined texture file. This is what allows swap texturepack and animate textures to work. - - -### Meshswap: -- **Purpose:** To automatically swap in extra assets such as 3D grass, light emitting torches and lamps, and so forth. Note that all the objects to be swapped in are in the blend file part of this download. Also note that swapping is done based on the name of the material. If you are unsure why your object is not swapping in, check the material name matches the counterpart object/material in the meshSwap.blend file. Note it can search for both append-able objects as well as groups, containing particles and so forth. Modifiers on the mesh in the original file will be brought over, so notice for example how the tall grass when replaced will be "pre-simulated" as it has displacement modifiers setup already with animation. -- **Step 0:** By default this is already done for you; set the MeshSwap blend path to the "mcprep_meshSwap.blend" or custom blend file, and make sure the world export has blocks of size 1m (100cm). -- **Step 1:** Select the objects that you wish to be meshSwapped. Swappable objects are determined *automatically* based on the contents of the blend file selected above. If an object is not found or swappable, it will just be skipped - no harm done by "over selecting" (so select all objects to make sure everything that can be swapped gets swapped!) -- **Step 2:** Press Mesh Swap (there will be a small delay, meshswapping large areas such as fields of grass may take awhile). - -*Setup your Mineways worlds in this fashion for best results.* - -![Mineways exporter settings](/visuals/mineways-settings.png?raw=true) - -*Setup your jmc2obj worlds in this fashion for best results.* - -![jmc2obj exporter settings](/visuals/jmc2obj-settings.png?raw=true) - -*Note: You can now also directly add meshswap blocks into the scene from the shift-A menu or the spawner:meshswap panel.* - -To add your own objects to meshswap (or groupswap): - -- **Step 1:** Check your imported world object and see the name of the material for the object you want to setup. You might think it is "glass plane", but if the importer names the material "glass_plane", you need to note this name down for step 3. -- **Step 2:** Model you object in the mcprep_meshSwap.blend file, or append it. -- **Step 3:** Rename your object to exactly match the previously noted material name. If you want to have a group swappable, then name the group to match the name above. - * So the MATERIAL name found in the 3D imported world should match the OBJECT name of the model in the meshswap file to work - * Note if both a group and an object have matching names, the script will prefer the group and meshswap that over the object. -- **Step 4:** Add necessary properties to special blocks as needed. See the meshSwap file included for examples, but the properties to add are: - * "variance": Objects with this property when meshswapped will have some x/y variance added. If the property is set to 1, it will also have (only negative) z variance. If it is set to 0, it will only have xy variance. For instance, tall_grass is given a value of 1 so some grass is shorter than others, but flowers are given a value of 0 so they always have the same height but still have some variance in placement. - * "edgeFloat": objects like vines, ladders, and lillypads which float off the edge of other blocks. - * "torchlike": objects that can have rotations like a torch on a wall. Objects with this property will be determined to be either on top of a block or placed on the side of another block according to the mesh. - * **Note:** there is no UI for adding properties to a group, so if you want to add a property to a group (say a torch which has a pre-animated light and particle system, as the included blend file does) you must go into the python console and add the property like so: bpy.data.groups['groupName']['propertyName'] = 1 (the value only matters for the variance property) - * **Example:** bpy.data.groups['torch']['torchlike'] = 1 will add the torchlike property to the torch group, allowing it to have correct rotations when meshSwapped in. - - -### Animate textures: -- **Purpose:** Replace still images with animated sequences, for livelier materials! This operator runs over all materials of all selected objects. For each *image pass* found within each material, it will attempt to replace the still image with the tiled counterpart in the active texture pack. Minecraft resource packs store animated textures in the form of long, vertical images. Blender internal and cycles work best when reading image sequences as separated files, so this function actually "exports" the image sequence from a single tiled image into a subfolder with one image per frame, and then assigns this to the image block in blender. Any tiled image is eligible to be animated this way, even if not normally an animated sequence in vanilla Minecraft. - -- **Step 0:** Prep materials first, if you haven't already; not required, but helpful. -- **Step 1:** Select the materials you want to animate. Try this on water, lava, and portals! Note, there is no harm in "over-selecting"; if you press animate textures on materials that cannot be animated (i.e. no tiled images to pull in), it will just skip over it. -- **Step 2:** From the popup, decide how and where you want the save the generated image sequence. - - "Next to current source image": This will export the sequence to a subfolder in the same directory as the current, existing image - - "Inside MCprep texture pack": This will export the sequence to a subfolder in active texture pack selected - - "Next to this blend file": This will export the sequence to a Textures folder placed next to the current saved blend file. - - Be careful when moving your blend file to other locations or other computers! Even if you use "pack images", image sequences **do not get packed** in the blender file. You must manually copy this folder to wherever you plan to render/open the file. For this purpose, such as render farms or multi-computer rendering, be sure to always set save location as "Next to this blend file" (preferred) or "Next to current source image". -- **Step 3:** If you have been running into issues, consider ticking the "clear cache" box (this will remove any previous or partially exported image sequences) -- **Step 3:** Press okay, and wait! Some notes: - - If you have a high-resolution texture pack selected, this could take some time. Blender will be unresponsive while this processes. - - This should only be slow the first time you animate textures, thereafter (with the same save-location selection) it will skip re-exporting and directly load the existing image sequence for each matching material. - - You can view progress in the console (on Windows, go to top bar >Windows > Toggle console) - -*Animate textures can be found under the World Imports - Advanced panel; or tick the box on prep materials.* -![Animate textures button](/visuals/animateTextures.png?raw=true) - -Using Mineways? Use the following world export settings to use this feature (key: use tiles): -![Mineways exporter settings](/visuals/mineways-settings.png?raw=true) - - -### Scale UV Faces: -- **Purpose:** To take a UV map and scale all of the individual UV faces about their own origins, similar to "Individual origins" transformation available in the 3D view for scaling, but is not available for connected faces in the UV editing window. This would be used if you are experiencing "bleeding" of a texture around the edges of a face, sometimes an artifact of how Blender treats low resolution images, or as a quick way to fix loosely-done UV unwrapping. -- **Step 1:** Select a mesh that already has a material and texture applied -- **Step 2:** Go into edit mode on this mesh -- **Step 3:** Press the Scale UV Face button from either location: - - 3D View > Toolshelf (left, press t) > MCprep Tab > World Imports - - Image Viewer > Toolshelf (left, press t) > Tools tab > Transform Panel, MCprep section -- **Step 4:** Adjust the scale factor in the redo last (or F6) window, defaults to 0.75 - - -### Select Alpha Faces: -- **Purpose:** To quickly select, or delete, all faces that fall on transparent pixels of the corresponding applied image for the active mesh. Useful if you want to speed up renders by not having to render transparent faces, or avoid other related issues. **Note**: The current implementation works well for "grid" unwrapped objects where the UV map has clear rows and columns aligned to the image axis, however it will still work to *some extent* with any unwrapped face UV shape over any image. -- **Step 1:** Select a mesh that already has a material and texture applied -- **Step 2:** Go into edit mode on this mesh -- **Step 3:** Press the Select Alpha Faces button from either location: - - 3D View > Toolshelf (left, press t) > MCprep Tab > World Imports - - Image Viewer > Toolshelf (left, press t) > Tools tab > Transform Panel, MCprep section -- **Step 4:** Adjust the properties in the redo last window, or by pressing F6 - - Delete faces: If checked, this will automatically delete the selected faces (setting will be saved for the current blender session) - - Threshold: From 0-1, consider the face as transparent if the average of the image pixels falling within the given face is below this threshold. - - -### Create MC Sky: -- **Purpose:** This operator provides options to add both simple and advanced, shader-driven skies to your worlds. There are two primary types of skies you can add. Works for Eevee, Cycles, and Blender Internal (no shader-driven option for adding sky/moon for Blender Internal) -- Adds a basic day-sky texture. Works for both cycles and blender internal, and creates a better starting point than the default gray world background. No option yet for setting other times of day. -- The optional settings: - - **Dynamic World**: This is an advanced node shader setup designed for both Cycles and Eevee. It allows for driving the sky colors and brightness based on the time of day, as driven by a time property which will appear in the panel after adding a dynamic world. This allows you to create day, morning, night, and sunrise/sunset scenes with ease, and furthermore even lets you freely animate the time such that you can create your own timelapse sequences. Dynamic skies also import sun lamps, and either shader-based or mesh-based sun/moon. You can even somewhat easily hack the shader to insert HDRs in place of the procedural blended sky colors, and have the shader automatically and smoothly blend between these images. As you change the time, the sun and moon (if enabled) and physical sun lamp follow the time of day in sync. Variants of the Dynamic World include: - - **With Shader sun/moon**: This unique feature allows you to have a sun and moon that will appear in the scene but at a distance of infinity, without having to change your camera render distance limit! - - **With mesh sun/moon**: The mesh is imported from a source library, and place into the scene around the origin of the scene. Note that, of course, your sun/moon objects exist in the scene like any other object, and so you make want to scale them out to limit the effect of parallax effects relative to the camera, and likewise update your camera’s render distance if you do scale out the distance of the sun/moon meshes from the camera. - - **No sun or moon** imported/setup - - **Static World**: This is a simpler shader setup and does not offer the ability to animate the time of day, but still is better than the default gray background! Best fit for a midday scene. Options include: - - With mesh sun/moon: Same description as in the section above. - - No sun or moon mesh imported - - Import cloud mesh - - Remove existing sun lamps (as dynamic and static sky will import their own sun lamps, you may want to use this to remove existing ones) - - -### Prep World: -- **Purpose:** This button found in the World Tools panel will assign nicer default world settings for rendering and animation, for both Blender Internal and Cycles. -- Note: Previously this operator added simple sky materials, now it is focused on only render settings. Use "Create MC World" above to initial world settings. -- Optimizes/improves render settings (cycles): - - Turns off reflective and refractive caustics (will increase speed over default) - - Increases light sampling threshold to a better balance (will increase speed over default) - - Sets max bounces to 8 (blender default is 12, and generally this number can typically be lowered further) - - Turns on Simplify and sets Simplify AO to level 2 for viewport and render (this will save on average 20-30% on render times with minimal impact; for much faster rendering, you can even set this lower to a level of 1, but be mindful of how shadows and reflections around objects with texture transparencies behave). -- Improves render settings (blender internal): - - Turns on AO with multiple of 0.1 (may cause renders to be slower, but generally nicer looking) - - Turns on environment global illumination (color inherited from sky settings). This may slow down renders, but they will be generally nicer and prevents any “pitch black” scenes. - - Turns on ray tracing and shadows (may cause renders to be slower, but generally nicer looking) - - If there is a sun in the scene, it will turn on “use sky blend” which will make the rotation of the sun lamp affect the sky color / sun glow position. - - -### Sync Materials -- **Purpose:** Quickly load in previously set up materials found in the active resource pack's `materials.blend`, and replace corresponding materials on selected objects. Found under the World Exporter > Advanced section, or in prep materials popup. -- **Step 1:** Select an object with materials you want to sync -- **Step 1:** Press Sync Materials. This will only affect materials on the selected objects. If nothing was matched to the `materials.blend` file, - -To edit the materials.blend file for syncing: - -- **Step 1:** Open the materials.blend file in the active resource pack, or create the file if adding it to another resource pack. This file should be placed in the root folder of the resource pack, i.e. next to the `pack.png` file and `assets` folder. -- **Step 2:** Technically optional but recommended: add a new cube for your material for quick previewing. Reset the UV layout in edit mode so each face takes up the whole texture. -- **Step 3:** Apply a material to this new object, either by: - 1) Appending from an existing file where you already created this material - 2) Usse the `Load Materials` panel to load the default MCprep material. Strongly suggested you change the resource pack (under advanced) to be `//` to tell blender to use the local director as the resource pack, so that it doesn't reference images outside the resource pack. -- **Step 3:** Edit the material shader nodes. ONLY the material itself will be synced. - - -### Skin Swapping: -- **Purpose:** To provide quick and easy skin changing on rigs and characters already in your blender scene. -- **Step 1:** Select your rig (or rigs) that you want to change the skin for. It works best if you select all of the objects of your rig (e.g. head, torso, legs etc) but it will work even if you only selected the armature (in that case, results are only visible in rendered view) -- **Step 2:** Select a skin from the skin file list under the tool menu: 3D view > MCprep tab > Skin Swapper Panel > UI List of skins, left click to select/highlight a skin - - *Don't see the skin you want?* Click "skin from file" to select one from your machine, or "skin from username" to download and apply a Minecraft user's skin, or go into advanced to Add Skin for future use without immediately applying it. -- **Step 3:** Press the button that says "Apply [skin name]" -- **Step 4:** You're done! If the user interface appears to not update, be sure to **check rendered view** (shift+z). Also note the default behavior is to make a *new* material. This way, if you had other characters with the same skin that you don't want changed, those are left alone. You can turn this off in the redo last menu or via F6. - - -### Load Material: -- **Purpose:** To quickly load/generate Minecraft materials, without having to first import a world obj. Follows convention of the active resource pack and general Prep Materials operator. -- **Step 2:** Select an object and go into the Properties > Material tab (this feature is NOT in the MCprep panel). -- **Step 2:** Load materials. Runs automatically when changing resource pack -- **Step 3:** Select the material you want to load with left click. You can scroll to find the desired material, or expand the search bar inside the bottom of the UI list box. -- **Step 4:** Select load - the material will *replace* the material in the current material slot. - - -### Mob Spawner: -- **Purpose:** To provide quick, one-click importing/linking of quality Minecraft mob and player rigs in blender. -- **Step 0:** By default this is already done for you; make sure the mob spawner path is a directory with valid blend files setup for linking (addon preferences > MCprep). After installing MCprep for the first time, this path will already be setup and valid pointing to the included rigs with this release, as defined in the credits section above. This rigs are place in the addon's local directory provided by blender and will not be placed anywhere else on the user's machine. Deleting the addon also deletes these rigs (careful if you're library linking!) -- **Step 1:** Either go to MCprep tab > spawner > click on Mob, or go to the shift-a menu: MCprep > mob spawner > [mob name] to instantly append or link in a rig. -- **Step 2:** Check the redo last menu (or press F6) for additional settings: - - Relocation: Change where the spawned rig appears. - - Cursor (default): Place the rig at the cursor's location - - Origin: Move the rig to the origin - - Offset root: Move the rig to the origin, but offset the root bone to the cursor's location (note: doesn't work with all rigs correctly right now, will be improved in the future) - - Library Link mob: If disabled, the group is appended (the groups is not kept so it can be appended multiple times). If enabled, the rig will be linked in and armatures auto-proxied. - - Be careful! If the blend file moves, the libraries will likely get broken unless a custom rigs folder is used with a local-relative path. - - Clear pose: clear to pose to rest. If false, the initial pose will be that found in the rig's source blend file. **Note:** some rigs have animations already setup, setting clear pose will also automatically clear any default animations. - - Prep materials: this will automatically run the prep materials function, noting this will regenerate cycles materials if cycles is the active render engine. - -*Mob Spawner Redo-last/F6 Options* -![Meshswap options](/visuals/spawnOptions.png?raw=true) - -#### To add your own rigs to the Mob Spawner: - -- **Step 1:** Make your rig, or download one you want to use! -- **Step 2:** Make sure all elements of the rig, ie all armatures, body parts, and extra objects, are added to a single group (select and press control+g) inside your rig file. The name of this group is what will appear under the shift-a menu, and typically matches the name of the file if there is just one rig per blend file. -- **Step 3:** Optional but useful, rename the root/main bone of the rig to one of [MAIN, root, base], used for relocation. Additionally, make the armature for animation named [name].arma where [name] exactly matches the name of the group. This is used for auto-proxying of the armature and relocation methods. -- **Step 4:** Optional, if you have a custom script for the rig, save it as an external file whose name matches the blend file exactly, but with .py instead of .blend, place this in the same folder as the blend file. - - Additionally, to auto import an icon with this rig, create a folder next tot he blend file called "icons" and create a file with the name that exactly matches the name of the rig's *group name*, e.g. Group name "Zombie Pigman" could have the icon file "zombie pigman.png" (not how this is not case-sensitive, but it is space-sensitive) -- **Step 5:** Either from Blender Preferences > Addon > MCprep preferences panel > "Install file for mob spawning" or from the MCprep 3D view tab, go to spawner > mob > menu "Install new mob". From there, use the file browser to select the blend file and install it! This copies the file to the current mob folder, by default inside the MCprep addon. -- **Alternative:** To specify a different, custom folder in a location of your choosing for mob spawning, simply change the "Mob spawner folder" path in the MCprep mob spawner advanced section (this setting is saved to blend file), or save a new default in the addon's preferences (becomes the default for all new blend scenes). -- Note: all groups inside installed blend files will appear for mob spawning. After installing a blend file, you do *not* need to save user preferences to use it in future blender sessions. - -#### Using multiple collections in your rig? - -Be aware of this behavior to ensure only one collection appears in the rig spawner (use one or the other): -- Option 1: Add "mcprep" to the name of your top-level collection to be imported -- Option 2: Add "mcskip" to *all* collections that should NOT be listed. - - -*Sometimes you may need to reload a rig cache, click this button if the correct rigs aren't appearing - or if you've just added new rigs to the folder outside of blender* - -![Reload rig cache](/visuals/reloadRigCache.png?raw=true) - - - -### Block Spawning - -Generate blocks from vanilla or other resource packs, or even from mods or other sources. **NOTE!** This is a newer part of MCprep, and because it is completely generalized, it's rather unstable. There are any known issues where blocks do not generate correctly, so bear this in mind ([see here](https://github.com/TheDuckCow/MCprep/issues/267)). - - -#### Spawn resource pack blocks -- **Purpose:** To be able to generate blocks defined by json files from the active resource pack -- **Step 1:** Go into Spawners, click the triangle next to Block (model) spawner. -- **Step 2:** Click a row to make active, you can search for a block by clicking the other smaller triangle icon inside the scroll view -- **Step 3:** Press "Place: {block name}" - - -#### Spawn blocks from json -- **Purpose:** To be able to generate blocks defined by json files -- **Step 1:** Go into Spawners, click the triangle next to Block (model) spawner -- **Step 2:** Press "Import model (.json)" to popup the filebrowser -- **Step 2:** Navigate to a .json file on your system -- **Step 2:** With the single json file selected, press "Import Model" - - -### Item Spawner: -- **Purpose:** To be able to quickly generate 3D Minecraft items from images. -- **Step 0:** By default this is already done for you; in advanced settings, point the folder to a valid Minecraft resource pack (with an items folder), or directly select the folder containing individual images per item. -- **Step 1:** Navigate to the spawner panel and select items; press load if needed -- **Step 2:** Select the item in the UI list, you can press the little plus at the bottom to open a free text search to hep bring up what you want -- **Step 3:** Press the Place: {item} button below -- **Step 4:** Modify redo last settings as needed (also accessible by pressing F6 after spawning). Options include: - - Maximum number of pixels: Will scale the image down if number of pixels exceeds this (unlikely for Minecraft resource packs) - - Thickness: If above 0, will add a solidify modifier with this level of thickness; can always be adjusted or removed later - - Alpha Threshold: At this level or lower of alpha values (0.0-1.0), delete the face from the resulting mesh. - - Use transparent pixels: If enabled, will setup the material so that transparent pixels will appear transparent in the render, otherwise will be solid. - - - -### Effects Spawner: -Quickly bring effects into your scene. - -#### Effects types -There are four different kinds of effects that MCprep supports: - -- **Geo node**: Load wide area effects driven by Geo Nodes (Blender 3.0+ only). These have the benefit of being deterministic, meaning no simulation occurs, and can follow the camera. This means no matter how fast your camera moves, you won't run into particles like snow not having fallen yet. -- **Particle**: Wide area effects like snow and rain, parented to the camera to simulate weather. -- **Collection**: Load pre-animated effects saved in collections (Blender 2.8+ only). Animations (both direct and indirect, such as particle systems) are automatically offset based on the current frame, making this much more useful that appending and trying to manually offset when it plays. Each imported combo collection+frame gets its own collection added and excluded, for reuse. -- **Image Sequence**: Load a target image sequence from a resource pack folder and import into blender inside a collection where each image is setup as a mesh plane. The animation can be sped up or slowed down at the time of import. -- **Particle Planes**: This does not show up in the UI scroll list. Instead, this is an operator button that when pressed adds a particle system based from a cut up version of a selected input image, and offset to the current timeline frame. - -#### Adding your own effects - -**Purpose:** Learn how to add your own effects to extend MCprep for your case. -- **Geo node effects:** Simply add another blend file with your geo node setup into the MCprep resources folder: MCprep_resources/effects/geonodes - - If you have a reusable node group where different parameters of the same group make different effects happen (e.g. calm rain vs windy rain), you can define a json file which matches exactly the blend file name, containing a mapping of settings to the node group in question. This will override all geo nodes that will be listed in MCprep. See the existing node group as an example. This is NOT required ot simply have a single geo node effect show up. - - Node groups are automatically parented to the active camera in the scene and meant to "follow" the view -- **Particle effects:** Simply add new particle systems to the same or new blend files in the MCprep resources folder: MCprep_resources/effects/particle. - - Your default emitter should have a size of 40x40m, so that MCprep can automatically maintain the same according density of particles. This size also helps ensure there is sufficient coverage no matter which direction the camera points or turns. -- **Collection effects:** Simply add new effects to new blend files in the MCprep resources folder: MCprep_resources/effects/collection. The animations are assumed to start at frame one, and offset accordingly when spawned in a scene later. -- **Image sequence effects:** These are read from the active resource pack's effects folder. Simply place your image sequence of files there and it should load in automatically. Alternatively, point the active resource pack to the parent of another effects resource pack folder (the active resource pack must be pointing to the top level of the resource pack still in order to recognize the folder for image sequence spawning) - - -### Entity Spawner: -- **Purpose:** To provide quick, one-click importing of pre-rigged entities that are not mobs or blocks -- **Step 1:** Navigate to the spawner panel and expand Entity Spawner -- **Step 2:** Click a row to select it -- **Step 3:** Spawn arrow! - -Under advanced settings, you can change your target entity file to point to another blend file, as well as force reload entities. - - -### Meshswap block Spawner: -- **Purpose:** To provide quick, one-click importing of meshswap 3D blocks and groups. -- **Step 0:** By default this is already done for you; make sure the meshswap file path is a directory with valid blend files setup for appending (addon preferences > MCprep). When installed, this path will already be setup and valid pointing to the included blocks with this release. -- **Step 1:** Navigate to the spawner panel and expand Meshswap Spawner -- **Step 2:** Select a row to spawn -- **Step 3:** Press "Place {block}" -- **Step 4:** Modify redo last settings as needed (also accessible by pressing F6 after spawning). Options include: - - Meshswap block: You can change here which block you wanted to add - - Location: Set the location where the block is placed - - Append layer: layer to append contents of the group to; default is layer 20, setting to 0 will use the currently active layer(s) as the layers for the group - - Prep materials: run prep materials on the imported objects, particularly useful if using cycles - - Snapping dropdown: Snap the placed block to a rounded coordinate. Optional offset by 0.5 as well - - Make real: Instance the groups so they are made real, thus allowing you to individually modify the objects within the group. Note: this may clear any pre-applied animation. - - -### Cycles Optimizer -- **Purpose:** To optimize Cycles render settings without sacrificing much quality -- **Step 1:** Navigate to the World Imports panel and find the "Cycles Optimizer" panel -- **Step 2:** Select the features you want to use -- **Step 3:** Hit the "Optimize Scene" button (It is recommended to do this twice. Once when prepping materials and once before rendering your final render) - -**Warning**: There is a section called unsafe features which contains options that may cause render issues. These features can being massive performance boosts but it's not recommended to use them unless you know what you're doing. Unsafe options include: - -- **Automatic Scrambling Distance:** Improves GPU performance but can cause artifacts. As such, if the optimizer reaches a scrambling multiplier of 0.7+, MCprep will automatically disable it. Please enable this before rendering your final render to reduce the chances of having artifacts: - -- **Preview Scrambling:** Enabled by default if you enable `Automatic Scrambling Distance`. This can cause a lot of flickering in rendered view (which is how Automatic Scrambling Distance works in the first place). - -![Optimizer panel](/visuals/optimizer-panel-settings.png?raw=true) - - -#### Cycles Optimizer Node Settings -The Cycles optimizer also comes with it some special node names to control how it interprets certain nodes. They are the following: -- `MCPREP_HOMOGENOUS_VOLUME`: if applied to a Volume Scatter, Volume Absorption, or Principled Volume node, it is treated as a homogeneous volume -- `MCPREP_NOT_HOMOGENOUS_VOLUME`: if applied to a Volume Scatter, Volume Absorption, or Principled Volume node, it is not treated as a homogeneous volume - -To use these settings, simply click on the node you want to edit, press N, and then edit the Name of the node - -**Important:** Make sure you edit the **Name** of the node, not the **Label** -![Using optimizer node settings](/visuals/optimizer-node-settings.png?raw=true) - -Known Bugs -====== -See all [known current bugs here](https://github.com/TheDuckCow/MCprep/issues?q=is%3Aissue+is%3Aopen+label%3Abug) - - -Future Plans -====== -Future development plans are now recorded and updated as milestones and enhancement issues on GitHub. Check [those out here](https://github.com/TheDuckCow/MCprep/milestones) - - -Additional Help -====== - -If you have troubles getting this addon to work, please contact me at support[at]TheDuckCow.com or [on twitter](https://twitter.com/TheDuckCow), and I will do my best to respond promptly to your questions. This addon is always being updated, but there may be some time between releases, so check back for updates (you can reference the version number and take last modified). Your feedback helps stabilize the addon and make it work better for everyone else! - -Moo-Ack! +## Why no builds? +Because this is not meant for normal use. If you know how to build MCprep itself though, using this should be simple enough. From d86f91be6ea798f1fe356fdfbde89587332f82d3 Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 22 Jan 2023 19:37:29 -0600 Subject: [PATCH 033/103] Added support for removing emission for simple materials --- MCprep_addon/materials/generate.py | 33 +++++++++++++++--------------- MCprep_addon/materials/prep.py | 8 +++++++- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 85966bc7..3305e39e 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -357,7 +357,7 @@ def matprep_internal(mat, passes, use_reflections, only_solid): def matprep_cycles( - mat, passes, use_reflections, use_principled, only_solid, pack_format): + mat, passes, use_reflections, use_principled, only_solid, pack_format, use_emission_nodes): """Determine how to prep or generate the cycles materials. Args: @@ -381,18 +381,18 @@ def matprep_cycles( # TODO: Update different options for water before enabling this # if use_reflections and checklist(canon, "water"): - # res = matgen_special_water(mat, passes) + # res = matgen_special_water(mat, passes) # if use_reflections and checklist(canon, "glass"): - # res = matgen_special_glass(mat, passes) + # res = matgen_special_glass(mat, passes) if pack_format == "simple" and util.bv28(): res = matgen_cycles_simple( - mat, passes, use_reflections, use_emission, only_solid, use_principled) + mat, passes, use_reflections, use_emission, only_solid, use_principled, use_emission_nodes) elif use_principled and hasattr(bpy.types, 'ShaderNodeBsdfPrincipled'): res = matgen_cycles_principled( - mat, passes, use_reflections, use_emission, only_solid, pack_format) + mat, passes, use_reflections, use_emission, only_solid, pack_format, use_emission_nodes) else: res = matgen_cycles_original( - mat, passes, use_reflections, use_emission, only_solid, pack_format) + mat, passes, use_reflections, use_emission, only_solid, pack_format, use_emission_nodes) return res @@ -1340,7 +1340,7 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): def matgen_cycles_simple( - mat, passes, use_reflections, use_emission, only_solid, use_principled): + mat, passes, use_reflections, use_emission, only_solid, use_principled, use_emission_nodes): """Generate principled cycles material.""" matGen = util.nameGeneralize(mat.name) @@ -1427,14 +1427,15 @@ def matgen_cycles_simple( mat.blend_method = 'HASHED' if hasattr(mat, "shadow_method"): mat.shadow_method = 'HASHED' - - if use_emission: - inputs = [inp.name for inp in principled.inputs] - if 'Emission Strength' in inputs: # Later 2.9 versions only. - principled.inputs['Emission Strength'].default_value = 1 - links.new( - nodeSaturateMix.outputs[saturateMixOut[0]], - principled.inputs["Emission"]) + + if use_emission_nodes: + if use_emission: + inputs = [inp.name for inp in principled.inputs] + if 'Emission Strength' in inputs: # Later 2.9 versions only. + principled.inputs['Emission Strength'].default_value = 1 + links.new( + nodeSaturateMix.outputs[saturateMixOut[0]], + principled.inputs["Emission"]) # reapply animation data if any to generated nodes apply_texture_animation_pass_settings(mat, animated_data) @@ -1908,7 +1909,7 @@ def matgen_special_water(mat, passes): saturateMixOut = get_node_socket(nodeSaturateMix, is_input=False) # Sets default values - nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending + nodeSaturateMix.inputs[0].default_value = 1.0 # Graystyle Blending nodeNormalInv.mapping.curves[1].points[0].location = (0, 1) nodeNormalInv.mapping.curves[1].points[1].location = (1, 0) nodeMixTrans.inputs[0].default_value = 0.8 diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 65088228..285dfd99 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -119,6 +119,11 @@ def pack_formats(self, context): description="Change the pack format when using a PBR resource pack.", items=pack_formats ) + useEmission = bpy.props.EnumProperty( + name="Use Emission", + description="Make emmisive materials emit light", + default=True + ) def draw_mats_common(self, context): @@ -149,6 +154,7 @@ def draw_mats_common(self, context): col.prop(self, "combineMaterials") row = self.layout.row() row.prop(self, "optimizeScene") + row.prop(self, "useEmission") class MCPREP_OT_prep_materials(bpy.types.Operator, McprepMaterialProps): @@ -239,7 +245,7 @@ def execute(self, context): elif engine == 'CYCLES' or engine == 'BLENDER_EEVEE': res = generate.matprep_cycles( mat, passes, self.useReflections, - self.usePrincipledShader, self.makeSolid, self.packFormat) + self.usePrincipledShader, self.makeSolid, self.packFormat, self.useEmission) if res == 0: count += 1 else: From bb6f3ff1ac8cb16e9087f11ea973b2c3cf875b36 Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 22 Jan 2023 19:45:32 -0600 Subject: [PATCH 034/103] pricipled node setups now can generate without emissive nodes --- MCprep_addon/materials/generate.py | 59 +++++++++++++++++------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 3305e39e..5d8d5e70 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1463,7 +1463,7 @@ def matgen_cycles_simple( def matgen_cycles_principled( - mat, passes, use_reflections, use_emission, only_solid, pack_format): + mat, passes, use_reflections, use_emission, only_solid, pack_format, use_emission_nodes): """Generate principled cycles material""" matGen = util.nameGeneralize(mat.name) @@ -1492,18 +1492,6 @@ def matgen_cycles_principled( principled = create_node( nodes, "ShaderNodeBsdfPrincipled", location=(120, 0)) - nodeEmit = create_node( - nodes, "ShaderNodeEmission", location=(120, 140)) - nodeEmitCam = create_node( - nodes, "ShaderNodeEmission", location=(120, 260)) - nodeMixCam = create_node( - nodes, "ShaderNodeMixShader", location=(320, 260)) - nodeFalloff = create_node( - nodes, "ShaderNodeLightFalloff", location=(-80, 320)) - nodeLightPath = create_node( - nodes, "ShaderNodeLightPath", location=(-320, 520)) - nodeMixEmit = create_node( - nodes, "ShaderNodeMixShader", location=(420, 0)) nodeTrans = create_node( nodes, "ShaderNodeBsdfTransparent", location=(420, 140)) nodeMixTrans = create_node( @@ -1531,16 +1519,40 @@ def matgen_cycles_principled( principled.inputs["Metallic"].default_value = 0 # Connect nodes - links.new(principled.outputs["BSDF"], nodeMixEmit.inputs[1]) - links.new(nodeLightPath.outputs["Is Camera Ray"], nodeMixCam.inputs["Fac"]) - links.new(nodeFalloff.outputs["Linear"], nodeEmit.inputs["Strength"]) - links.new(nodeEmit.outputs["Emission"], nodeMixCam.inputs[1]) - links.new(nodeEmitCam.outputs["Emission"], nodeMixCam.inputs[2]) - links.new(nodeMixCam.outputs["Shader"], nodeMixEmit.inputs[2]) links.new(nodeTrans.outputs["BSDF"], nodeMixTrans.inputs[1]) - links.new(nodeMixEmit.outputs["Shader"], nodeMixTrans.inputs[2]) links.new(nodeMixTrans.outputs["Shader"], nodeOut.inputs[0]) + if use_emission_nodes: + # Create emission nodes + nodeEmit = create_node( + nodes, "ShaderNodeEmission", location=(120, 140)) + nodeEmitCam = create_node( + nodes, "ShaderNodeEmission", location=(120, 260)) + nodeMixCam = create_node( + nodes, "ShaderNodeMixShader", location=(320, 260)) + nodeFalloff = create_node( + nodes, "ShaderNodeLightFalloff", location=(-80, 320)) + nodeLightPath = create_node( + nodes, "ShaderNodeLightPath", location=(-320, 520)) + nodeMixEmit = create_node( + nodes, "ShaderNodeMixShader", location=(420, 0)) + + # Create the links + links.new(principled.outputs["BSDF"], nodeMixEmit.inputs[1]) + links.new(nodeLightPath.outputs["Is Camera Ray"], nodeMixCam.inputs["Fac"]) + links.new(nodeFalloff.outputs["Linear"], nodeEmit.inputs["Strength"]) + links.new(nodeEmit.outputs["Emission"], nodeMixCam.inputs[1]) + links.new(nodeEmitCam.outputs["Emission"], nodeMixCam.inputs[2]) + links.new(nodeMixCam.outputs["Shader"], nodeMixEmit.inputs[2]) + links.new(nodeMixEmit.outputs["Shader"], nodeMixTrans.inputs[2]) + + if use_emission: + nodeMixEmit.inputs[0].default_value = 1 + else: + nodeMixEmit.inputs[0].default_value = 0 + + + nodeInputs = [ [ principled.inputs["Base Color"], @@ -1592,12 +1604,7 @@ def matgen_cycles_principled( # both work fine with depth of field. # but, BLEND does NOT work well with Depth of Field or layering - - if use_emission: - nodeMixEmit.inputs[0].default_value = 1 - else: - nodeMixEmit.inputs[0].default_value = 0 - + # reapply animation data if any to generated nodes apply_texture_animation_pass_settings(mat, animated_data) From 4f76d0929214aeef3299259e9da1af456515029b Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 22 Jan 2023 19:52:51 -0600 Subject: [PATCH 035/103] Restored README to original state --- README.md | 587 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 570 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a8c3417f..88b567fb 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,578 @@ -# MCprep Kaion -## What is it? -A fork of MCprep focused on advanced features. Most of these features have been suggested in the past but haven't been implemented due to the following: -* Finer details haven't been worked out (most common reason) -* Requires external Python libraries (which can be hard to justify the extra work during installation) -* Low priority (features that are nice and as such aren't worth implementing) +MCprep Addon +====== +[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/mb8hBUC) +[![MCprep License](https://img.shields.io/github/license/TheDuckCow/MCprep)](https://www.gnu.org/licenses/gpl-3.0) +![MCprep Stars](https://img.shields.io/github/stars/TheDuckCow/MCprep) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/TheDuckCow/MCprep/blob/master/CONTRIBUTING.md) -This fork mostly aims to be a proof of concept, so any features that rely on assets (like meshswap) are missing. -## Is it right for me? -MCprep Kaion is not meant for normal use, I suggest using [mainline MCprep](https://github.com/TheDuckCow/MCprep). If you depend on the features that MCprep Kaion provides, it's simply best to suggest the feature on the [MCprep server](https://discord.gg/mb8hBUC) +MCprep is an addon dedicated to speeding up the workflow of Minecraft animators in Blender by automatically fixing up materials and providing other tools such as mob spawing, effects spawning, etc. -## Aren't you one of the MCprep maintainers? Why does this exist? -To answer the first question, yes I am, though I don't contribute much to MCprep anymore, see [here](https://standingpadanimations.github.io/posts/goodbye-for-now/) for more. The gist is that I left the Minecraft rendering community due to some drama, and in the process moved away from MCprep as well. +## Installing MCprep +### Click the link below and download the .zip file (re-zip if auto-unzipped into a folder necessary), install into blender (2.78 through 3.2 supported) -The idea for a fork came when I made the optimizer. I originally wanted to make a version of MCprep with far more power (yet destructive) optimizations. Eventually I also wanted to add features that in hindsight would take a while before implementation details were finalized. +[![Install MCprep](/visuals/mcprep_download.png)](https://theduckcow.com/dev/blender/mcprep-download/) -After I left Minecraft rendering, I wanted to bring some of those features to MCprep, and created this fork. +*By downloading and installing, you agree to the following [privacy policy](https://theduckcow.com/privacy-policy).* **[Watch this video](https://www.youtube.com/watch?v=i6Ne07-eIyI) on how to install if running into troubles.** -## Will some of these features be merged eventually? -Since MCprep Kaion is best to be treated as a proof of concept fork for ideas, the hope is that most (if not all) of the changes made here will be merged upstream. -## Why no builds? -Because this is not meant for normal use. If you know how to build MCprep itself though, using this should be simple enough. +It should remain a zip folder. In blender, go to preferences, then the addons tab, and at the bottom of the window install from file. Select the .zip file. **NOTE:** _Blender may not automatically enable the addon. If the MCprep addon is not already shown in the window after installing, search for it at left and then ensure the check box is enabled._ **Save user preferences** to keep it enabled next time blender opens. + +*Again, please download from the link above or the releases page, __not__ by clicking `download zip` button above.* + +*The preferences panel should look like this after installing the zip file* +![Installed MCprep 2.7](/visuals/install.png?raw=true) + +*Or, if using Blender 2.8, follow these similar steps* +![Installed MCprep 2.8](/visuals/install_28.png?raw=true) + +**If you like the addon, [please consider donating](http://bit.ly/donate2TheDuckCow) for the continued quality development! [Share this addon](https://twitter.com/intent/tweet?text=Make+easier+Minecraft+renders+using+the+MCprep+addon+bit.ly/MCprep+by+@TheDuckCow) so others can benefit from it! Or help by [taking this quick survey](http://bit.ly/MCprepSurvey)!** + + +Learn how to use MCprep +====== + +*[Short, 1-minute videos showcasing how to use Blender+MCprep features](https://bit.ly/MCprepTutorials)* + +[![Tutorial playlist](https://img.youtube.com/vi/62fVXVAO13A/0.jpg)](https://bit.ly/MCprepTutorials) + +*[Additional Playlist of MCprep videos/demos](https://www.youtube.com/playlist?list=PL8X_CzUEVBfaajwyguIj_utPXO4tEOr7a)* + + +About MCprep +====== + +This is a blender python addon to improve and automate many aspects of creating Minecraft renders and animations. It can help you improt world's exported from Minecraft, set up better materials, importing mobs and items, and set up proxy characters for animation, and even includes default animations for common blocks/mobs like tall grass, torches, and mobs like the bat or blaze. This addon assumes you have already exported a Minecraft world to an OBJ file. While the script should work for any world importer, it has been tested and developed based on the jmc2obj and Mineways tools for exporting Minecraft worlds to obj files. + +This addon is made to work with an asset library directory, from which models and groups are linked or imported from. This library blend file is included, but does not have all types of blocks generated yet. This will be improved in the future. + +This addon is compatible officially down to 2.72 official builds, and up to blender 3.00. Not all features are available in all versions, try to use the latest available blender. *Run into any problems? [Submit bugs/issues here](https://github.com/TheDuckCow/MCprep/issues).* + + +Feature list +====== + +| World Imports | Description | +|----------|:-------------:| +| Prep Materials | Improves materials from world imports, and allows one-click switching from cycles & blender internal materials. *Note, this does not* create *materials, only modifies existing ones.* | +| Swap Texture Pack | *Initial support only for jmc2obj world exports.* Using a valid Minecraft resource pack, you can now completely replace the textures of the imported world with another pack - you can even changed individual blocks at a time. | +| Animate textures | *Initial support only for jmc2obj world exports.* With a valid (or the MCprep default) resource pack selected, you can replace still images with their animated versions. Works great to put motion back into lava, water, portals and other blocks for any kind of resource pack. | +| Combine materials/images | Consolidates duplicate materials and images down to the smallest number of unique datablocks. *Note: combine images is only available on blender 2.78+* | +| Improve UI | A shortcut to quickly improve viewport settings for Minecraft sets. Sets textured solid mode & turns off mipmaps | +| Mesh Swap | Allows you to replace simple models from 3D exported worlds with more intricate 3D models | +| Scale UV Faces | Allows you to scale all UV faces of a mesh about their origin. Must be in edit mode of a mesh. | +| Select Alpha Faces | Allows you to select or delete mesh faces that are transparent in the applied image texture. Must be in edit mode of a mesh. | +| Sync Materials | Search for the `materials.blend` file within the active texture pack, and for any matching materials found in this file, import and overwrite the current file's version of that same material. Found under the World Exporter > Advanced section or in prep materials popup. | + + +| World Tools | Description | +|----------|:-------------:| +| Create MC World | Operator which allows user to create either a static or dynamic sky. Options exist to also add clouds, remove existing sun lamps, and define how the Sun/Moon is imported (either as a shader, mesh, or neither). If a dynamic sky is created, a time-control slider will also be displayed below. | +| Prep World | This operator makes several updates to either Blender Internal, Cycles, or Eevee scenes that are optimal and in most cases will notably improve render times (particularly for cycles) and increase overall aesthetic. | + + +| Skin Swapper | Description | +|----------|:-------------:| +| Apply [skin] | Applies the currently list-selected skin to the actively selected rigs. | +| Skin from file | Loads in a skin texture from file and applies to the actively selected rigs. *Note: Also gets added to skin list* | +| Skin from username | Downloads a skin based on a play name and applies to the actively selected rigs. *Note: Also gets added to skin list* | +| + sign | Install a custom skin into the list without applying to a rig | + +*Note: only 1.8 skin layouts are supported at this time, in the future an auto-conversion feature will likely be implemented* + + +| Spawner | Description | +|----------|:-------------:| +| Spawn: [rig] | Based on the actively selected rig, from an according spawning rig category, add the mob/character into the scene. These are fully rigged characters. Using the plus sign, you can even install your own rigs (rig must be part of a group). For quicker mapping, you can even set an entire folder and it will auto-create subcategories of rigs based on folders. | +| Spawn: meshswap block | Place a block from the meshswap file into the 3D scene. Currently is limited to only having meshswap groups (e.g. torches and fire) and not objects (e.g. not supporting grass and flowers yet).| +| Spawn: items | Convert any image icon into a 3D mesh, with faces per each pixel and transparent faces already removed. Defaults to loading Minecraft items.| + + + +| Material Library | Description | +|----------|:-------------:| +| Load material | Generate the selected material and apply it to the selected material slot of the active object. Follows convention of the active resource pack and general Prep Materials operator. | + + +### Spawner mobs included in the current version of MCprep +![included-mobs](/visuals/MCprep_mobs_included.png?raw=true) + + +CREDIT +====== +While this addon is released as open source software, the assets are being released as [Creative Commons Attributions, CC-BY](https://creativecommons.org/licenses/by/3.0/us/). If you use MeshSwap, **please credit the creators** by linking to this page wherever your project may appear: [http://github.com/TheDuckCow/MCprep](https://github.com/TheDuckCow/MCprep) + +Meshswap Block models developed by [Patrick W. Crawford](https://twitter.com/TheDuckCow), [SilverC16](http://youtube.com/user/silverC16), and [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse). + + +| Player Rigs | Creator | +|----------|:-------------:| +| Fancy Feet Player | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) ([Rig link](http://bit.ly/MinecraftRig)) | +| Fancy Feet Slim Player | Modified by [Jeremy Putnam](http://www.blendswap.com/user/lorddon) (MCprep exclusive) | +| Fancy Feet Armor Player | Modified by [zNightlord](https://twitter.com/znightTrung) (MCprep exclusive) | +| Simple Player (modified) | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Simple Slim Player (modified) | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) + TheDuckCow (MCprep exclusive) | +| Shapekey Player | Derived from [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home), shapekeys and animation by [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) (MCprep exclusive) | +| Story Mode Rig | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | +| VMcomix Steve (rounded) | [VMcomix](https://www.youtube.com/user/VMComix) ([Rig link](http://vmcomix.blogspot.com/)) | + + +| Passive Rigs | Creator | +|----------|:-------------:| +| Allay | [zNightlord](https://twitter.com/znightTrung) | +| Axolotl | [Breadcrumb](https://www.youtube.com/channel/UCT1_5td4SWg67fhElGz1S4A) | +| Bat | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) (MCprep exclusive) | +| Cat | [TRPHB Animation](https://youtube.com/c/TRPHBAnimation) | +| Cod fish | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Chicken | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | +| Cow | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | +| Dolphin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Fox | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Frog | [zNightlord](https://twitter.com/znightTrung) | +| Glow squid | [TRPHB Animation](https://youtube.com/c/TRPHBAnimation) | +| Goat | [TRPHB Animation](https://youtube.com/c/TRPHBAnimation) | +| Mooshroom (red/brown) | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Parrot | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Pig | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | +| Panda | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Pufferfish| [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Rabbit | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | +| Salmon | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Sheep | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | +| Squid | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | +| Tropical Fish | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Cod fish | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Turtle | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | + + +| Hostile Rigs | Creator | +|----------|:-------------:| +| Bee | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Blaze | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | +| Cave Spider | [Austin Prescott](https://youtu.be/0xA4-yMr2KY) | +| Creeper | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) (MCprep exclusive) | +| Drowned | [thefunnypie2 + HissingCreeper](https://github.com/TheDuckCow/MCprep/issues/160#issuecomment-931923101) | +| Eldar Guardian | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Ender Dragon | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Enderman | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79750)) | +| Endermite | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | +| Evoker | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Ghast | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) (No direct link yet) | +| Guardian | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79729)) | +| Hoglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Husk | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Magma Cube | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Piglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Pillager | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Polar bear | [PixelFrosty](https://twitter.com/Pixel_Frosty) ([Demo/download](https://www.youtube.com/watch?v=kbt5HWjafBk))| +| Phantom | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Ravager | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Shulker | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79729)) | +| Silverfish | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | +| Skeleton | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link, outdated](http://www.blendswap.com/blends/view/79495)) | +| Slime | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Spider | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | +| Stray | [thefunnypie2 + Trainguy9512](https://github.com/TheDuckCow/MCprep/issues/160#issuecomment-931923101) | +| Strider | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Vindicator | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Warden | [DigDanAnimates](https://twitter.com/DigDanAnimates) | +| Witch | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Wither | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Wither Skeleton | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Vindicator | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Zoglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Zombie | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | +| Zombie Pigman | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Zombie Villager | [thefunnypie2 + HissingCreeper](https://github.com/TheDuckCow/MCprep/issues/160#issuecomment-931923101) | +| Zombified Piglin | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | + + + +| Friendly/Utility Rigs | Creator | +|----------|:-------------:| +| Horse | [Patrick W. Crawford](http://www.youtube.com/TheDuckCow) ([Rig link](http://www.blendswap.com/blends/view/73064)) | +| Iron Golem | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link](http://www.blendswap.com/blends/view/79455)) | +| Llama | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Ocelot | [HissingCreeper](https://www.youtube.com/channel/UCHV3_5kFI93fFOl6KbmhkQA) (No direct link yet) | +| Simple Villager (Dynamic skin) | [Th3pooka](https://twitter.com/Th3Pooka) | +| Snow Golem | [Nils Söderman (rymdnisse)](http://youtube.com/rymdnisse) ([Rig link](http://rymdnisse.net/downloads/minecraft-blender-rig.html)) | +| Villager | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Wandering Trader | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Wolf | [Trainguy9512](https://www.youtube.com/channel/UCktn-etC2h25hMTk1tIz7IQ) ([Rig link, outdated](http://www.blendswap.com/blends/view/79628)) | + + +| Entities | Creator | +|----------|:-------------:| +| Arrow | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | +| Boat | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | +| Bow | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | +| Crossbow | [TRPHB Animation](https://www.youtube.com/c/TRPHBAnimation/) | +| Minecart | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| Shield | [Ageofherosmedia](https://github.com/TheDuckCow/MCprep/issues/245#issuecomment-998745223) | +| Spyglass | [Banana-Blu](https://github.com/Banana-Blu) | + + +| Effects | Creator | +|----------|:-------------:| +| Geo node effects | [Th3pooka](https://twitter.com/Th3Pooka) | +| TNT effects (both) | [TheDuckCow](http://www.youtube.com/TheDuckCow) | +| Rain + Snow particles | [TheDuckCow](http://www.youtube.com/TheDuckCow) | + + +| Meshswap Blocks | Creator | +|----------|:-------------:| +| Campfire | [BoxScape Studios](https://sites.google.com/view/boxscape-studios/home) ([Pack link](https://sites.google.com/view/boxscape-studios/downloads)) | +| All others | [TheDuckCow](http://www.youtube.com/TheDuckCow) | + + +If you use any of these rigs in your animation, give credit to the according creator or by directly referring back to this readme file which contains all the credits. *Models have been slightly modified to function best and consistently with the MCprep addon.* + + +Thank you to all the contributors to this project! It will continue to grow and cover more assets in the near future. + + + +How to use this addon +====== + + +### Prep materials: +- **Purpose:** To automatically setup better, crisp materials for rendering Minecraft, low resolution textures. It works for both Blender internal as well as cycles, and more recently the cycles Principled shader. It will even selectively turn 'shiny' materials into reflecting materials, as well as 'bright' materials into emitting materials. Currently, the list of these materials is hard-coded into a json file, with names matching the material names for both jmc2obj and Mineways. You can modify this file for your own needs, but largely should be fine with the default, and will be updated (overwritten) as new updates are added. +- **Step 1:** Export your world to an OBJ or other general 3D formats. I use jmc2obj, but Mineways or other such formats should be fine as well. +- **Step 2:** Import the world into blender (e.g. via file > import > obj, or whatever according format) +- **Step 3:** Select all, or select the objects that have the material you want to fix. Materials can be all separate objects or the same object, it does not matter. +- **Step 4:** Under the MCprep panel, press "Prep Materials". +- **Step 5:** Change settings (optional, defaults are good!) and press ok - and wait a moment while the addon does the rest! Settings + +*Settings popup for prep materials.* + +![Prep material settings](/visuals/prepMaterialsSettings.png?raw=true) + +Setting options: + - **Use principled shader:** Cycles only and 2.79 and up, use the more physically accurate Principled shader node. If disabled, it will default back to previous material setups. The principled shader is, however, preferred. + - **Use reflections:** Add extra settings to make material more reflective. For Blender Internal, enable reflections. For cycles materials, increase reflection alpha so that even fully transparent sections of image (e.g. in glass) will show some reflections. + - **Animate textures:** See the section below, this performs the same operation after prepping materials, with the default behavior of "save to next to current source image", which is likely in the texture pack itself + - **Find missing images:** Look to replace any missing images with their counterparts in the active resource pack, which by default is vanilla Minecraft textures as ship with MCprep + - **Use extra maps:** If extra passes like normal and specular maps are available with the selected resource pack, load them into the material (note: the default MCprep texture pack will not have these passes) + - **Improve UI:** Does not impact existing materials, but just sets the viewport to a nice minimum default to see the result of prepping materials - but always checked rendered mode to see how they truly look in the end + - **Combine materials:** This will go through the entire blend file and attempt to merge together same-named materials (e.g. grass and grass.001 and grass.002 will all be consolidated down into one shared material, though something like grass-edit would be left alone) + - **Sync materials:** Look for any matching material names (after casting to Minecraft canonical names) in the materials.blend file (if presnet) under the active resource pack. If found, replace this local material with that from the materials.blend file. + + +### Swap texture pack +- **Purpose:** Replace the images in a blend file with those from another Minecraft resource pack. Operates on selected objects and their respective materials, so you can mix and match different resource packs on a per-material basis this way. +- **Step 1:** Select all materials you wish to swap texture packs for +- **Step 2:** Prep materials on these objects if you haven't already (prepping a second time will do no harm); not doing this could have mixed results. +- **Step 3:** Press on the swap texture pack button in the 3D view > MCprep tab > World Imports panel +- **Step 4:** In this popup, the folder will default to the MCprep resource pack (ie default Vanilla Minecraft); navigate to an *extracted* zip folder of a valid resource pack. You can select any sub-folder, but to be safe, select the folder level such that the the "pack.png" and "assets" sub-folder are visible as rows. +- **Step 5:** Decide if you want to enable/disable pulling in extra passes (e.g. normal and specular, if they exist) and whether to animate textures upon swapping. Both of these tickboxes, on by default, are in the left-hand sidebar. + +Using Mineways? Use the following world export settings to use this feature: +![Mineways exporter settings](/visuals/mineways-settings.png?raw=true) + +Note: Use tiles for exporting to create individual files per block instead of one combined texture file. This is what allows swap texturepack and animate textures to work. + + +### Meshswap: +- **Purpose:** To automatically swap in extra assets such as 3D grass, light emitting torches and lamps, and so forth. Note that all the objects to be swapped in are in the blend file part of this download. Also note that swapping is done based on the name of the material. If you are unsure why your object is not swapping in, check the material name matches the counterpart object/material in the meshSwap.blend file. Note it can search for both append-able objects as well as groups, containing particles and so forth. Modifiers on the mesh in the original file will be brought over, so notice for example how the tall grass when replaced will be "pre-simulated" as it has displacement modifiers setup already with animation. +- **Step 0:** By default this is already done for you; set the MeshSwap blend path to the "mcprep_meshSwap.blend" or custom blend file, and make sure the world export has blocks of size 1m (100cm). +- **Step 1:** Select the objects that you wish to be meshSwapped. Swappable objects are determined *automatically* based on the contents of the blend file selected above. If an object is not found or swappable, it will just be skipped - no harm done by "over selecting" (so select all objects to make sure everything that can be swapped gets swapped!) +- **Step 2:** Press Mesh Swap (there will be a small delay, meshswapping large areas such as fields of grass may take awhile). + +*Setup your Mineways worlds in this fashion for best results.* + +![Mineways exporter settings](/visuals/mineways-settings.png?raw=true) + +*Setup your jmc2obj worlds in this fashion for best results.* + +![jmc2obj exporter settings](/visuals/jmc2obj-settings.png?raw=true) + +*Note: You can now also directly add meshswap blocks into the scene from the shift-A menu or the spawner:meshswap panel.* + +To add your own objects to meshswap (or groupswap): + +- **Step 1:** Check your imported world object and see the name of the material for the object you want to setup. You might think it is "glass plane", but if the importer names the material "glass_plane", you need to note this name down for step 3. +- **Step 2:** Model you object in the mcprep_meshSwap.blend file, or append it. +- **Step 3:** Rename your object to exactly match the previously noted material name. If you want to have a group swappable, then name the group to match the name above. + * So the MATERIAL name found in the 3D imported world should match the OBJECT name of the model in the meshswap file to work + * Note if both a group and an object have matching names, the script will prefer the group and meshswap that over the object. +- **Step 4:** Add necessary properties to special blocks as needed. See the meshSwap file included for examples, but the properties to add are: + * "variance": Objects with this property when meshswapped will have some x/y variance added. If the property is set to 1, it will also have (only negative) z variance. If it is set to 0, it will only have xy variance. For instance, tall_grass is given a value of 1 so some grass is shorter than others, but flowers are given a value of 0 so they always have the same height but still have some variance in placement. + * "edgeFloat": objects like vines, ladders, and lillypads which float off the edge of other blocks. + * "torchlike": objects that can have rotations like a torch on a wall. Objects with this property will be determined to be either on top of a block or placed on the side of another block according to the mesh. + * **Note:** there is no UI for adding properties to a group, so if you want to add a property to a group (say a torch which has a pre-animated light and particle system, as the included blend file does) you must go into the python console and add the property like so: bpy.data.groups['groupName']['propertyName'] = 1 (the value only matters for the variance property) + * **Example:** bpy.data.groups['torch']['torchlike'] = 1 will add the torchlike property to the torch group, allowing it to have correct rotations when meshSwapped in. + + +### Animate textures: +- **Purpose:** Replace still images with animated sequences, for livelier materials! This operator runs over all materials of all selected objects. For each *image pass* found within each material, it will attempt to replace the still image with the tiled counterpart in the active texture pack. Minecraft resource packs store animated textures in the form of long, vertical images. Blender internal and cycles work best when reading image sequences as separated files, so this function actually "exports" the image sequence from a single tiled image into a subfolder with one image per frame, and then assigns this to the image block in blender. Any tiled image is eligible to be animated this way, even if not normally an animated sequence in vanilla Minecraft. + +- **Step 0:** Prep materials first, if you haven't already; not required, but helpful. +- **Step 1:** Select the materials you want to animate. Try this on water, lava, and portals! Note, there is no harm in "over-selecting"; if you press animate textures on materials that cannot be animated (i.e. no tiled images to pull in), it will just skip over it. +- **Step 2:** From the popup, decide how and where you want the save the generated image sequence. + - "Next to current source image": This will export the sequence to a subfolder in the same directory as the current, existing image + - "Inside MCprep texture pack": This will export the sequence to a subfolder in active texture pack selected + - "Next to this blend file": This will export the sequence to a Textures folder placed next to the current saved blend file. + - Be careful when moving your blend file to other locations or other computers! Even if you use "pack images", image sequences **do not get packed** in the blender file. You must manually copy this folder to wherever you plan to render/open the file. For this purpose, such as render farms or multi-computer rendering, be sure to always set save location as "Next to this blend file" (preferred) or "Next to current source image". +- **Step 3:** If you have been running into issues, consider ticking the "clear cache" box (this will remove any previous or partially exported image sequences) +- **Step 3:** Press okay, and wait! Some notes: + - If you have a high-resolution texture pack selected, this could take some time. Blender will be unresponsive while this processes. + - This should only be slow the first time you animate textures, thereafter (with the same save-location selection) it will skip re-exporting and directly load the existing image sequence for each matching material. + - You can view progress in the console (on Windows, go to top bar >Windows > Toggle console) + +*Animate textures can be found under the World Imports - Advanced panel; or tick the box on prep materials.* +![Animate textures button](/visuals/animateTextures.png?raw=true) + +Using Mineways? Use the following world export settings to use this feature (key: use tiles): +![Mineways exporter settings](/visuals/mineways-settings.png?raw=true) + + +### Scale UV Faces: +- **Purpose:** To take a UV map and scale all of the individual UV faces about their own origins, similar to "Individual origins" transformation available in the 3D view for scaling, but is not available for connected faces in the UV editing window. This would be used if you are experiencing "bleeding" of a texture around the edges of a face, sometimes an artifact of how Blender treats low resolution images, or as a quick way to fix loosely-done UV unwrapping. +- **Step 1:** Select a mesh that already has a material and texture applied +- **Step 2:** Go into edit mode on this mesh +- **Step 3:** Press the Scale UV Face button from either location: + - 3D View > Toolshelf (left, press t) > MCprep Tab > World Imports + - Image Viewer > Toolshelf (left, press t) > Tools tab > Transform Panel, MCprep section +- **Step 4:** Adjust the scale factor in the redo last (or F6) window, defaults to 0.75 + + +### Select Alpha Faces: +- **Purpose:** To quickly select, or delete, all faces that fall on transparent pixels of the corresponding applied image for the active mesh. Useful if you want to speed up renders by not having to render transparent faces, or avoid other related issues. **Note**: The current implementation works well for "grid" unwrapped objects where the UV map has clear rows and columns aligned to the image axis, however it will still work to *some extent* with any unwrapped face UV shape over any image. +- **Step 1:** Select a mesh that already has a material and texture applied +- **Step 2:** Go into edit mode on this mesh +- **Step 3:** Press the Select Alpha Faces button from either location: + - 3D View > Toolshelf (left, press t) > MCprep Tab > World Imports + - Image Viewer > Toolshelf (left, press t) > Tools tab > Transform Panel, MCprep section +- **Step 4:** Adjust the properties in the redo last window, or by pressing F6 + - Delete faces: If checked, this will automatically delete the selected faces (setting will be saved for the current blender session) + - Threshold: From 0-1, consider the face as transparent if the average of the image pixels falling within the given face is below this threshold. + + +### Create MC Sky: +- **Purpose:** This operator provides options to add both simple and advanced, shader-driven skies to your worlds. There are two primary types of skies you can add. Works for Eevee, Cycles, and Blender Internal (no shader-driven option for adding sky/moon for Blender Internal) +- Adds a basic day-sky texture. Works for both cycles and blender internal, and creates a better starting point than the default gray world background. No option yet for setting other times of day. +- The optional settings: + - **Dynamic World**: This is an advanced node shader setup designed for both Cycles and Eevee. It allows for driving the sky colors and brightness based on the time of day, as driven by a time property which will appear in the panel after adding a dynamic world. This allows you to create day, morning, night, and sunrise/sunset scenes with ease, and furthermore even lets you freely animate the time such that you can create your own timelapse sequences. Dynamic skies also import sun lamps, and either shader-based or mesh-based sun/moon. You can even somewhat easily hack the shader to insert HDRs in place of the procedural blended sky colors, and have the shader automatically and smoothly blend between these images. As you change the time, the sun and moon (if enabled) and physical sun lamp follow the time of day in sync. Variants of the Dynamic World include: + - **With Shader sun/moon**: This unique feature allows you to have a sun and moon that will appear in the scene but at a distance of infinity, without having to change your camera render distance limit! + - **With mesh sun/moon**: The mesh is imported from a source library, and place into the scene around the origin of the scene. Note that, of course, your sun/moon objects exist in the scene like any other object, and so you make want to scale them out to limit the effect of parallax effects relative to the camera, and likewise update your camera’s render distance if you do scale out the distance of the sun/moon meshes from the camera. + - **No sun or moon** imported/setup + - **Static World**: This is a simpler shader setup and does not offer the ability to animate the time of day, but still is better than the default gray background! Best fit for a midday scene. Options include: + - With mesh sun/moon: Same description as in the section above. + - No sun or moon mesh imported + - Import cloud mesh + - Remove existing sun lamps (as dynamic and static sky will import their own sun lamps, you may want to use this to remove existing ones) + + +### Prep World: +- **Purpose:** This button found in the World Tools panel will assign nicer default world settings for rendering and animation, for both Blender Internal and Cycles. +- Note: Previously this operator added simple sky materials, now it is focused on only render settings. Use "Create MC World" above to initial world settings. +- Optimizes/improves render settings (cycles): + - Turns off reflective and refractive caustics (will increase speed over default) + - Increases light sampling threshold to a better balance (will increase speed over default) + - Sets max bounces to 8 (blender default is 12, and generally this number can typically be lowered further) + - Turns on Simplify and sets Simplify AO to level 2 for viewport and render (this will save on average 20-30% on render times with minimal impact; for much faster rendering, you can even set this lower to a level of 1, but be mindful of how shadows and reflections around objects with texture transparencies behave). +- Improves render settings (blender internal): + - Turns on AO with multiple of 0.1 (may cause renders to be slower, but generally nicer looking) + - Turns on environment global illumination (color inherited from sky settings). This may slow down renders, but they will be generally nicer and prevents any “pitch black” scenes. + - Turns on ray tracing and shadows (may cause renders to be slower, but generally nicer looking) + - If there is a sun in the scene, it will turn on “use sky blend” which will make the rotation of the sun lamp affect the sky color / sun glow position. + + +### Sync Materials +- **Purpose:** Quickly load in previously set up materials found in the active resource pack's `materials.blend`, and replace corresponding materials on selected objects. Found under the World Exporter > Advanced section, or in prep materials popup. +- **Step 1:** Select an object with materials you want to sync +- **Step 1:** Press Sync Materials. This will only affect materials on the selected objects. If nothing was matched to the `materials.blend` file, + +To edit the materials.blend file for syncing: + +- **Step 1:** Open the materials.blend file in the active resource pack, or create the file if adding it to another resource pack. This file should be placed in the root folder of the resource pack, i.e. next to the `pack.png` file and `assets` folder. +- **Step 2:** Technically optional but recommended: add a new cube for your material for quick previewing. Reset the UV layout in edit mode so each face takes up the whole texture. +- **Step 3:** Apply a material to this new object, either by: + 1) Appending from an existing file where you already created this material + 2) Usse the `Load Materials` panel to load the default MCprep material. Strongly suggested you change the resource pack (under advanced) to be `//` to tell blender to use the local director as the resource pack, so that it doesn't reference images outside the resource pack. +- **Step 3:** Edit the material shader nodes. ONLY the material itself will be synced. + + +### Skin Swapping: +- **Purpose:** To provide quick and easy skin changing on rigs and characters already in your blender scene. +- **Step 1:** Select your rig (or rigs) that you want to change the skin for. It works best if you select all of the objects of your rig (e.g. head, torso, legs etc) but it will work even if you only selected the armature (in that case, results are only visible in rendered view) +- **Step 2:** Select a skin from the skin file list under the tool menu: 3D view > MCprep tab > Skin Swapper Panel > UI List of skins, left click to select/highlight a skin + - *Don't see the skin you want?* Click "skin from file" to select one from your machine, or "skin from username" to download and apply a Minecraft user's skin, or go into advanced to Add Skin for future use without immediately applying it. +- **Step 3:** Press the button that says "Apply [skin name]" +- **Step 4:** You're done! If the user interface appears to not update, be sure to **check rendered view** (shift+z). Also note the default behavior is to make a *new* material. This way, if you had other characters with the same skin that you don't want changed, those are left alone. You can turn this off in the redo last menu or via F6. + + +### Load Material: +- **Purpose:** To quickly load/generate Minecraft materials, without having to first import a world obj. Follows convention of the active resource pack and general Prep Materials operator. +- **Step 2:** Select an object and go into the Properties > Material tab (this feature is NOT in the MCprep panel). +- **Step 2:** Load materials. Runs automatically when changing resource pack +- **Step 3:** Select the material you want to load with left click. You can scroll to find the desired material, or expand the search bar inside the bottom of the UI list box. +- **Step 4:** Select load - the material will *replace* the material in the current material slot. + + +### Mob Spawner: +- **Purpose:** To provide quick, one-click importing/linking of quality Minecraft mob and player rigs in blender. +- **Step 0:** By default this is already done for you; make sure the mob spawner path is a directory with valid blend files setup for linking (addon preferences > MCprep). After installing MCprep for the first time, this path will already be setup and valid pointing to the included rigs with this release, as defined in the credits section above. This rigs are place in the addon's local directory provided by blender and will not be placed anywhere else on the user's machine. Deleting the addon also deletes these rigs (careful if you're library linking!) +- **Step 1:** Either go to MCprep tab > spawner > click on Mob, or go to the shift-a menu: MCprep > mob spawner > [mob name] to instantly append or link in a rig. +- **Step 2:** Check the redo last menu (or press F6) for additional settings: + - Relocation: Change where the spawned rig appears. + - Cursor (default): Place the rig at the cursor's location + - Origin: Move the rig to the origin + - Offset root: Move the rig to the origin, but offset the root bone to the cursor's location (note: doesn't work with all rigs correctly right now, will be improved in the future) + - Library Link mob: If disabled, the group is appended (the groups is not kept so it can be appended multiple times). If enabled, the rig will be linked in and armatures auto-proxied. + - Be careful! If the blend file moves, the libraries will likely get broken unless a custom rigs folder is used with a local-relative path. + - Clear pose: clear to pose to rest. If false, the initial pose will be that found in the rig's source blend file. **Note:** some rigs have animations already setup, setting clear pose will also automatically clear any default animations. + - Prep materials: this will automatically run the prep materials function, noting this will regenerate cycles materials if cycles is the active render engine. + +*Mob Spawner Redo-last/F6 Options* +![Meshswap options](/visuals/spawnOptions.png?raw=true) + +#### To add your own rigs to the Mob Spawner: + +- **Step 1:** Make your rig, or download one you want to use! +- **Step 2:** Make sure all elements of the rig, ie all armatures, body parts, and extra objects, are added to a single group (select and press control+g) inside your rig file. The name of this group is what will appear under the shift-a menu, and typically matches the name of the file if there is just one rig per blend file. +- **Step 3:** Optional but useful, rename the root/main bone of the rig to one of [MAIN, root, base], used for relocation. Additionally, make the armature for animation named [name].arma where [name] exactly matches the name of the group. This is used for auto-proxying of the armature and relocation methods. +- **Step 4:** Optional, if you have a custom script for the rig, save it as an external file whose name matches the blend file exactly, but with .py instead of .blend, place this in the same folder as the blend file. + - Additionally, to auto import an icon with this rig, create a folder next tot he blend file called "icons" and create a file with the name that exactly matches the name of the rig's *group name*, e.g. Group name "Zombie Pigman" could have the icon file "zombie pigman.png" (not how this is not case-sensitive, but it is space-sensitive) +- **Step 5:** Either from Blender Preferences > Addon > MCprep preferences panel > "Install file for mob spawning" or from the MCprep 3D view tab, go to spawner > mob > menu "Install new mob". From there, use the file browser to select the blend file and install it! This copies the file to the current mob folder, by default inside the MCprep addon. +- **Alternative:** To specify a different, custom folder in a location of your choosing for mob spawning, simply change the "Mob spawner folder" path in the MCprep mob spawner advanced section (this setting is saved to blend file), or save a new default in the addon's preferences (becomes the default for all new blend scenes). +- Note: all groups inside installed blend files will appear for mob spawning. After installing a blend file, you do *not* need to save user preferences to use it in future blender sessions. + +#### Using multiple collections in your rig? + +Be aware of this behavior to ensure only one collection appears in the rig spawner (use one or the other): +- Option 1: Add "mcprep" to the name of your top-level collection to be imported +- Option 2: Add "mcskip" to *all* collections that should NOT be listed. + + +*Sometimes you may need to reload a rig cache, click this button if the correct rigs aren't appearing - or if you've just added new rigs to the folder outside of blender* + +![Reload rig cache](/visuals/reloadRigCache.png?raw=true) + + + +### Block Spawning + +Generate blocks from vanilla or other resource packs, or even from mods or other sources. **NOTE!** This is a newer part of MCprep, and because it is completely generalized, it's rather unstable. There are any known issues where blocks do not generate correctly, so bear this in mind ([see here](https://github.com/TheDuckCow/MCprep/issues/267)). + + +#### Spawn resource pack blocks +- **Purpose:** To be able to generate blocks defined by json files from the active resource pack +- **Step 1:** Go into Spawners, click the triangle next to Block (model) spawner. +- **Step 2:** Click a row to make active, you can search for a block by clicking the other smaller triangle icon inside the scroll view +- **Step 3:** Press "Place: {block name}" + + +#### Spawn blocks from json +- **Purpose:** To be able to generate blocks defined by json files +- **Step 1:** Go into Spawners, click the triangle next to Block (model) spawner +- **Step 2:** Press "Import model (.json)" to popup the filebrowser +- **Step 2:** Navigate to a .json file on your system +- **Step 2:** With the single json file selected, press "Import Model" + + +### Item Spawner: +- **Purpose:** To be able to quickly generate 3D Minecraft items from images. +- **Step 0:** By default this is already done for you; in advanced settings, point the folder to a valid Minecraft resource pack (with an items folder), or directly select the folder containing individual images per item. +- **Step 1:** Navigate to the spawner panel and select items; press load if needed +- **Step 2:** Select the item in the UI list, you can press the little plus at the bottom to open a free text search to hep bring up what you want +- **Step 3:** Press the Place: {item} button below +- **Step 4:** Modify redo last settings as needed (also accessible by pressing F6 after spawning). Options include: + - Maximum number of pixels: Will scale the image down if number of pixels exceeds this (unlikely for Minecraft resource packs) + - Thickness: If above 0, will add a solidify modifier with this level of thickness; can always be adjusted or removed later + - Alpha Threshold: At this level or lower of alpha values (0.0-1.0), delete the face from the resulting mesh. + - Use transparent pixels: If enabled, will setup the material so that transparent pixels will appear transparent in the render, otherwise will be solid. + + + +### Effects Spawner: +Quickly bring effects into your scene. + +#### Effects types +There are four different kinds of effects that MCprep supports: + +- **Geo node**: Load wide area effects driven by Geo Nodes (Blender 3.0+ only). These have the benefit of being deterministic, meaning no simulation occurs, and can follow the camera. This means no matter how fast your camera moves, you won't run into particles like snow not having fallen yet. +- **Particle**: Wide area effects like snow and rain, parented to the camera to simulate weather. +- **Collection**: Load pre-animated effects saved in collections (Blender 2.8+ only). Animations (both direct and indirect, such as particle systems) are automatically offset based on the current frame, making this much more useful that appending and trying to manually offset when it plays. Each imported combo collection+frame gets its own collection added and excluded, for reuse. +- **Image Sequence**: Load a target image sequence from a resource pack folder and import into blender inside a collection where each image is setup as a mesh plane. The animation can be sped up or slowed down at the time of import. +- **Particle Planes**: This does not show up in the UI scroll list. Instead, this is an operator button that when pressed adds a particle system based from a cut up version of a selected input image, and offset to the current timeline frame. + +#### Adding your own effects + +**Purpose:** Learn how to add your own effects to extend MCprep for your case. +- **Geo node effects:** Simply add another blend file with your geo node setup into the MCprep resources folder: MCprep_resources/effects/geonodes + - If you have a reusable node group where different parameters of the same group make different effects happen (e.g. calm rain vs windy rain), you can define a json file which matches exactly the blend file name, containing a mapping of settings to the node group in question. This will override all geo nodes that will be listed in MCprep. See the existing node group as an example. This is NOT required ot simply have a single geo node effect show up. + - Node groups are automatically parented to the active camera in the scene and meant to "follow" the view +- **Particle effects:** Simply add new particle systems to the same or new blend files in the MCprep resources folder: MCprep_resources/effects/particle. + - Your default emitter should have a size of 40x40m, so that MCprep can automatically maintain the same according density of particles. This size also helps ensure there is sufficient coverage no matter which direction the camera points or turns. +- **Collection effects:** Simply add new effects to new blend files in the MCprep resources folder: MCprep_resources/effects/collection. The animations are assumed to start at frame one, and offset accordingly when spawned in a scene later. +- **Image sequence effects:** These are read from the active resource pack's effects folder. Simply place your image sequence of files there and it should load in automatically. Alternatively, point the active resource pack to the parent of another effects resource pack folder (the active resource pack must be pointing to the top level of the resource pack still in order to recognize the folder for image sequence spawning) + + +### Entity Spawner: +- **Purpose:** To provide quick, one-click importing of pre-rigged entities that are not mobs or blocks +- **Step 1:** Navigate to the spawner panel and expand Entity Spawner +- **Step 2:** Click a row to select it +- **Step 3:** Spawn arrow! + +Under advanced settings, you can change your target entity file to point to another blend file, as well as force reload entities. + + +### Meshswap block Spawner: +- **Purpose:** To provide quick, one-click importing of meshswap 3D blocks and groups. +- **Step 0:** By default this is already done for you; make sure the meshswap file path is a directory with valid blend files setup for appending (addon preferences > MCprep). When installed, this path will already be setup and valid pointing to the included blocks with this release. +- **Step 1:** Navigate to the spawner panel and expand Meshswap Spawner +- **Step 2:** Select a row to spawn +- **Step 3:** Press "Place {block}" +- **Step 4:** Modify redo last settings as needed (also accessible by pressing F6 after spawning). Options include: + - Meshswap block: You can change here which block you wanted to add + - Location: Set the location where the block is placed + - Append layer: layer to append contents of the group to; default is layer 20, setting to 0 will use the currently active layer(s) as the layers for the group + - Prep materials: run prep materials on the imported objects, particularly useful if using cycles + - Snapping dropdown: Snap the placed block to a rounded coordinate. Optional offset by 0.5 as well + - Make real: Instance the groups so they are made real, thus allowing you to individually modify the objects within the group. Note: this may clear any pre-applied animation. + + +### Cycles Optimizer +- **Purpose:** To optimize Cycles render settings without sacrificing much quality +- **Step 1:** Navigate to the World Imports panel and find the "Cycles Optimizer" panel +- **Step 2:** Select the features you want to use +- **Step 3:** Hit the "Optimize Scene" button (It is recommended to do this twice. Once when prepping materials and once before rendering your final render) + +**Warning**: There is a section called unsafe features which contains options that may cause render issues. These features can being massive performance boosts but it's not recommended to use them unless you know what you're doing. Unsafe options include: + +- **Automatic Scrambling Distance:** Improves GPU performance but can cause artifacts. As such, if the optimizer reaches a scrambling multiplier of 0.7+, MCprep will automatically disable it. Please enable this before rendering your final render to reduce the chances of having artifacts: + +- **Preview Scrambling:** Enabled by default if you enable `Automatic Scrambling Distance`. This can cause a lot of flickering in rendered view (which is how Automatic Scrambling Distance works in the first place). + +![Optimizer panel](/visuals/optimizer-panel-settings.png?raw=true) + + +#### Cycles Optimizer Node Settings +The Cycles optimizer also comes with it some special node names to control how it interprets certain nodes. They are the following: +- `MCPREP_HOMOGENOUS_VOLUME`: if applied to a Volume Scatter, Volume Absorption, or Principled Volume node, it is treated as a homogeneous volume +- `MCPREP_NOT_HOMOGENOUS_VOLUME`: if applied to a Volume Scatter, Volume Absorption, or Principled Volume node, it is not treated as a homogeneous volume + +To use these settings, simply click on the node you want to edit, press N, and then edit the Name of the node + +**Important:** Make sure you edit the **Name** of the node, not the **Label** +![Using optimizer node settings](/visuals/optimizer-node-settings.png?raw=true) + +Known Bugs +====== +See all [known current bugs here](https://github.com/TheDuckCow/MCprep/issues?q=is%3Aissue+is%3Aopen+label%3Abug) + + +Future Plans +====== +Future development plans are now recorded and updated as milestones and enhancement issues on GitHub. Check [those out here](https://github.com/TheDuckCow/MCprep/milestones) + + +Additional Help +====== + +If you have troubles getting this addon to work, please contact me at support[at]TheDuckCow.com or [on twitter](https://twitter.com/TheDuckCow), and I will do my best to respond promptly to your questions. This addon is always being updated, but there may be some time between releases, so check back for updates (you can reference the version number and take last modified). Your feedback helps stabilize the addon and make it work better for everyone else! + +Moo-Ack! From f49b555f106bcbe02b40b9f0998509d0ac268ecd Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 22 Jan 2023 19:53:52 -0600 Subject: [PATCH 036/103] Added an extra argument, still need to figure out how this function works --- MCprep_addon/materials/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 5d8d5e70..9bbf4673 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1612,7 +1612,7 @@ def matgen_cycles_principled( def matgen_cycles_original( - mat, passes, use_reflections, use_emission, only_solid, pack_format): + mat, passes, use_reflections, use_emission, only_solid, pack_format, use_emission_nodes): """Generate principled cycles material""" matGen = util.nameGeneralize(mat.name) From 83127a14ab60dc6caef4db933d7aed5d05d3faf4 Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 3 Feb 2023 15:58:38 -0600 Subject: [PATCH 037/103] Fixed massive bug --- MCprep_addon/materials/prep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 285dfd99..ca32a8b9 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -119,7 +119,7 @@ def pack_formats(self, context): description="Change the pack format when using a PBR resource pack.", items=pack_formats ) - useEmission = bpy.props.EnumProperty( + useEmission = bpy.props.BoolProperty( name="Use Emission", description="Make emmisive materials emit light", default=True From 3eec63db3b96255307c897a6bf37e9891ab594e4 Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Feb 2023 20:15:59 -0600 Subject: [PATCH 038/103] Added extemely basic support for header info, but still some issues --- MCprep_addon/world_tools.py | 67 +++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 88ba51cf..747fa054 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -63,6 +63,7 @@ def get_time_object(): return time_obj_cache +OBJ_HEADER_TEXTURE_KEY = "TEXTURE_TYPE" def detect_world_exporter(filepath): """Detect whether Mineways or jmc2obj was used, based on prefix info. @@ -74,15 +75,28 @@ def detect_world_exporter(filepath): A valid string value for preferences ENUM MCprep_exporter_type """ with open(filepath, 'r') as obj_fd: + # Header options for OBJs + header_options = { + OBJ_HEADER_TEXTURE_KEY : "", + } try: header = obj_fd.readline() + if 'mineways' in header.lower(): + # form of: # Wavefront OBJ file made by Mineways version 5.10... + for line in obj_fd: + if line.startswith("# File type:"): + header = line + + # The issue here is that Mineways has changed how the header is generated. As such, we're limited with only a couple of OBJs, some from 2020 and some from 2023, so we'll assume people are using an up to date version. + if "textures to three large images" in header or "full color texture patterns": # If a texture atlas is used + header_options[OBJ_HEADER_TEXTURE_KEY] = "ATLAS" + elif "Export individual textures" in header or "tiles for textures" in header: # If the OBJ uses individual textures + header_options[OBJ_HEADER_TEXTURE_KEY] = "INDIVIDUAL_TILES" + return 'Mineways', header_options except UnicodeDecodeError: print("failed to read first line of obj: " + filepath) - return '(choose)' - if 'mineways' in header.lower(): - # form of: # Wavefront OBJ file made by Mineways version 5.10... - return 'Mineways' - return 'jmc2obj' + return '(choose)', header_options + return 'jmc2obj', header_options # ----------------------------------------------------------------------------- @@ -387,7 +401,8 @@ def execute(self, context): return {'CANCELLED'} prefs = util.get_user_preferences(context) - prefs.MCprep_exporter_type = detect_world_exporter(self.filepath) + prefs.MCprep_exporter_type, header_info = detect_world_exporter(self.filepath) + print(header_info) if util.bv28(): self.split_world_by_material(context) @@ -863,34 +878,34 @@ def create_dynamic_world(self, context, blendfile, wname): # # update drivers, needed if time has changed vs import source # if context.scene.world.node_tree.animation_data: - # # context.scene.world.node_tree.animation_data.drivers[0].update() - # drivers = context.scene.world.node_tree.animation_data.drivers[0] - # drivers.driver.variables[0].targets[0].id = time_obj - # # nope, still doesn't work. + # # context.scene.world.node_tree.animation_data.drivers[0].update() + # drivers = context.scene.world.node_tree.animation_data.drivers[0] + # drivers.driver.variables[0].targets[0].id = time_obj + # # nope, still doesn't work. # if needed: create time object and setup drivers # if not time_obj: - # conf.log("Creating time_obj") - # time_obj = bpy.data.objects.new('MCprep Time Control', None) - # util.obj_link_scene(time_obj, context) - # global time_obj_cache - # time_obj_cache = time_obj - # if hasattr(time_obj, "empty_draw_type"): # 2.7 - # time_obj.empty_draw_type = 'SPHERE' - # else: # 2.8 - # time_obj.empty_display_type = 'SPHERE' + # conf.log("Creating time_obj") + # time_obj = bpy.data.objects.new('MCprep Time Control', None) + # util.obj_link_scene(time_obj, context) + # global time_obj_cache + # time_obj_cache = time_obj + # if hasattr(time_obj, "empty_draw_type"): # 2.7 + # time_obj.empty_draw_type = 'SPHERE' + # else: # 2.8 + # time_obj.empty_display_type = 'SPHERE' # first, get the driver # if (not world.node_tree.animation_data - # or not world.node_tree.animation_data.drivers - # or not world.node_tree.animation_data.drivers[0].driver): - # conf.log("Could not get driver from imported dynamic world") - # self.report({'WARNING'}, "Could not update driver for dynamic world") - # driver = None + # or not world.node_tree.animation_data.drivers + # or not world.node_tree.animation_data.drivers[0].driver): + # conf.log("Could not get driver from imported dynamic world") + # self.report({'WARNING'}, "Could not update driver for dynamic world") + # driver = None # else: - # driver = world.node_tree.animation_data.drivers[0].driver + # driver = world.node_tree.animation_data.drivers[0].driver # if driver and driver.variables[0].targets[0].id_type == 'OBJECT': - # driver.variables[0].targets[0].id = time_obj + # driver.variables[0].targets[0].id = time_obj # add driver to control obj's x rotation return obj_list From e72a66f827b96a3b44651ebaacc0f8470c85a964 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 14:13:29 -0600 Subject: [PATCH 039/103] Got the file type to be detect correctly and move much of MCprep's OBJ options into a class --- MCprep_addon/world_tools.py | 70 ++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 747fa054..9440f517 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -18,6 +18,7 @@ import os import math +import re import bpy from bpy_extras.io_utils import ExportHelper, ImportHelper @@ -63,7 +64,44 @@ def get_time_object(): return time_obj_cache -OBJ_HEADER_TEXTURE_KEY = "TEXTURE_TYPE" +class ObjHeaderOptions: + def __init__(self): + self._exporter = None + self._file_type = None + + """ + Wrapper functions to avoid typos causing issues + """ + def set_mineways(self): + self._exporter = "Mineways" + def set_jmc2obj(self): + self._exporter = "jmc1obj" + + def set_atlas(self): + self._file_type = "ATLAS" + def set_seperated(self): + self._file_type = "INDIVIDUAL_TILES" + + """ + Determines if the OBJ is compatible with textureswap + """ + def texture_swap_compatible(self): + if self._file_type == "INDIVIDUAL_TILES": + return True + return False + + """ + Returns the exporter used + """ + def exporter(self): + return self._exporter if self._exporter is not None else "(choose)" + + """ + Returns the type of textures + """ + def texture_type(self): + return self._file_type if self._file_type is not None else "NONE" + def detect_world_exporter(filepath): """Detect whether Mineways or jmc2obj was used, based on prefix info. @@ -75,29 +113,28 @@ def detect_world_exporter(filepath): A valid string value for preferences ENUM MCprep_exporter_type """ with open(filepath, 'r') as obj_fd: - # Header options for OBJs - header_options = { - OBJ_HEADER_TEXTURE_KEY : "", - } + header_options = ObjHeaderOptions() try: header = obj_fd.readline() if 'mineways' in header.lower(): - # form of: # Wavefront OBJ file made by Mineways version 5.10... + # form of: # Wavefront OBJ file made by Mineways version 5.10... for line in obj_fd: if line.startswith("# File type:"): header = line # The issue here is that Mineways has changed how the header is generated. As such, we're limited with only a couple of OBJs, some from 2020 and some from 2023, so we'll assume people are using an up to date version. - if "textures to three large images" in header or "full color texture patterns": # If a texture atlas is used - header_options[OBJ_HEADER_TEXTURE_KEY] = "ATLAS" - elif "Export individual textures" in header or "tiles for textures" in header: # If the OBJ uses individual textures - header_options[OBJ_HEADER_TEXTURE_KEY] = "INDIVIDUAL_TILES" - return 'Mineways', header_options + atlas = re.compile(r'\btextures to three large images|full color texture patterns\b') + tiles = re.compile(r'\bexport individual textures|tiles for textures\b') + if re.search(atlas, header.lower()) is not None: # If a texture atlas is used + header_options.set_atlas() + elif re.search(tiles, header.lower()) is not None: # If the OBJ uses individual textures + header_options.set_seperated() + + return header_options except UnicodeDecodeError: print("failed to read first line of obj: " + filepath) - return '(choose)', header_options - return 'jmc2obj', header_options - + return header_options + return header_options # ----------------------------------------------------------------------------- # open mineways/jmc2obj related @@ -401,8 +438,9 @@ def execute(self, context): return {'CANCELLED'} prefs = util.get_user_preferences(context) - prefs.MCprep_exporter_type, header_info = detect_world_exporter(self.filepath) - print(header_info) + header_info = detect_world_exporter(self.filepath) + prefs.MCprep_exporter_type = header_info.exporter() + print(header_info.texture_type()) if util.bv28(): self.split_world_by_material(context) From c86e37162e7c697ab822f7f0fd3b5026bb103cd4 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 14:47:17 -0600 Subject: [PATCH 040/103] Moved ObjHeaderOptions to conf.py --- MCprep_addon/conf.py | 41 ++++++++++++++++++++++++++++++++++ MCprep_addon/materials/prep.py | 4 +++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/MCprep_addon/conf.py b/MCprep_addon/conf.py index 7a40de58..9b3116ca 100644 --- a/MCprep_addon/conf.py +++ b/MCprep_addon/conf.py @@ -32,6 +32,44 @@ # ----------------------------------------------------------------------------- +class ObjHeaderOptions: + def __init__(self): + self._exporter = None + self._file_type = None + + """ + Wrapper functions to avoid typos causing issues + """ + def set_mineways(self): + self._exporter = "Mineways" + def set_jmc2obj(self): + self._exporter = "jmc2obj" + + def set_atlas(self): + self._file_type = "ATLAS" + def set_seperated(self): + self._file_type = "INDIVIDUAL_TILES" + + """ + Determines if the OBJ is compatible with textureswap + """ + def texture_swap_compatible(self): + if self._file_type == "INDIVIDUAL_TILES": + return True + return False + + """ + Returns the exporter used + """ + def exporter(self): + return self._exporter if self._exporter is not None else "(choose)" + + """ + Returns the type of textures + """ + def texture_type(self): + return self._file_type if self._file_type is not None else "NONE" + def init(): # ----------------------------------------------- @@ -87,6 +125,9 @@ def init(): global preview_collections preview_collections = {} + global obj_header + obj_header = ObjHeaderOptions() + # ----------------------------------------------- # For initializing the custom icons # ----------------------------------------------- diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 65088228..6e6579ef 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -391,7 +391,9 @@ class MCPREP_OT_swap_texture_pack( @classmethod def poll(cls, context): addon_prefs = util.get_user_preferences(context) - return addon_prefs.MCprep_exporter_type != "(choose)" + if addon_prefs.MCprep_exporter_type != "(choose)": + return conf.obj_header.texture_swap_compatible() + return False def draw(self, context): row = self.layout.row() From 12081dbdae88afdf350eda7dc4de95c695234596 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 14:47:36 -0600 Subject: [PATCH 041/103] Changed some stuff --- MCprep_addon/world_tools.py | 63 ++++++------------------------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 9440f517..f1e4ff78 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -36,7 +36,6 @@ time_obj_cache = None - def get_time_object(): """Returns the time object if present in the file""" global time_obj_cache # to avoid re parsing every time @@ -63,60 +62,18 @@ def get_time_object(): return time_obj_cache - -class ObjHeaderOptions: - def __init__(self): - self._exporter = None - self._file_type = None - - """ - Wrapper functions to avoid typos causing issues - """ - def set_mineways(self): - self._exporter = "Mineways" - def set_jmc2obj(self): - self._exporter = "jmc1obj" - - def set_atlas(self): - self._file_type = "ATLAS" - def set_seperated(self): - self._file_type = "INDIVIDUAL_TILES" - - """ - Determines if the OBJ is compatible with textureswap - """ - def texture_swap_compatible(self): - if self._file_type == "INDIVIDUAL_TILES": - return True - return False - - """ - Returns the exporter used - """ - def exporter(self): - return self._exporter if self._exporter is not None else "(choose)" - - """ - Returns the type of textures - """ - def texture_type(self): - return self._file_type if self._file_type is not None else "NONE" - def detect_world_exporter(filepath): """Detect whether Mineways or jmc2obj was used, based on prefix info. Primary heruistic: if detect Mineways header, assert Mineways, else assume jmc2obj. All Mineways exports for a long time have prefix info set in the obj file as comments. - - Returns: - A valid string value for preferences ENUM MCprep_exporter_type """ with open(filepath, 'r') as obj_fd: - header_options = ObjHeaderOptions() try: header = obj_fd.readline() if 'mineways' in header.lower(): + conf.obj_header.set_mineways() # form of: # Wavefront OBJ file made by Mineways version 5.10... for line in obj_fd: if line.startswith("# File type:"): @@ -126,15 +83,14 @@ def detect_world_exporter(filepath): atlas = re.compile(r'\btextures to three large images|full color texture patterns\b') tiles = re.compile(r'\bexport individual textures|tiles for textures\b') if re.search(atlas, header.lower()) is not None: # If a texture atlas is used - header_options.set_atlas() + conf.obj_header.set_atlas() elif re.search(tiles, header.lower()) is not None: # If the OBJ uses individual textures - header_options.set_seperated() - - return header_options + conf.obj_header.set_seperated() + return except UnicodeDecodeError: print("failed to read first line of obj: " + filepath) - return header_options - return header_options + return + conf.obj_header.set_jmc2obj() # ----------------------------------------------------------------------------- # open mineways/jmc2obj related @@ -303,7 +259,6 @@ def execute(self, context): # Additional world tools # ----------------------------------------------------------------------------- - class MCPREP_OT_import_world_split(bpy.types.Operator, ImportHelper): """Imports an obj file, and auto splits it by material""" bl_idname = "mcprep.import_world_split" @@ -438,9 +393,9 @@ def execute(self, context): return {'CANCELLED'} prefs = util.get_user_preferences(context) - header_info = detect_world_exporter(self.filepath) - prefs.MCprep_exporter_type = header_info.exporter() - print(header_info.texture_type()) + detect_world_exporter(self.filepath) + print(conf.obj_header.exporter()) + prefs.MCprep_exporter_type = conf.obj_header.exporter() if util.bv28(): self.split_world_by_material(context) From 97cb0c2d3dc99fc067af02dac128a5ef4e4111c3 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 14:47:44 -0600 Subject: [PATCH 042/103] UI now reflects the header --- MCprep_addon/mcprep_ui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 3e4640d9..65888505 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -722,6 +722,8 @@ def draw(self, context): col = split.column(align=True) col.label(text="MCprep tools") col.operator("mcprep.prep_materials", text="Prep Materials") + if not conf.obj_header.texture_swap_compatible(): + col.label(text="OBJ not exported with the correct settings for textureswap") p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path if context.mode == "OBJECT": From cbef5ebf50651a14333cdfb5db518eeb76de619e Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 16:38:18 -0600 Subject: [PATCH 043/103] Saved OBJ header properties to OBJ and change UI based on that --- MCprep_addon/materials/prep.py | 45 +++++++++++++++++++++++++++++----- MCprep_addon/mcprep_ui.py | 9 +++++-- MCprep_addon/world_tools.py | 7 ++++-- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 6e6579ef..10d50f25 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -111,9 +111,9 @@ def pack_formats(self, context): "pack's materials.blend file"), default=True) # newDefault = bpy.props.BoolProperty( - # name="Use custom default material", - # description="Use a custom default material if you have one set up", - # default=False) + # name="Use custom default material", + # description="Use a custom default material if you have one set up", + # default=False) packFormat = bpy.props.EnumProperty( name="Pack Format", description="Change the pack format when using a PBR resource pack.", @@ -130,8 +130,25 @@ def draw_mats_common(self, context): col.prop(self, "usePrincipledShader") col.prop(self, "useReflections") col.prop(self, "makeSolid") - col.prop(self, "animateTextures") - col.prop(self, "autoFindMissingTextures") + if len(context.selected_objects): + file_types = { + "ATLAS" : 0, + "INDIVIDUAL" : 0 + } + for obj in context.selected_objects: + # If the header exists then we should be fine + if obj["MCPREP_OBJ_HEADER"] is not None: + if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": + file_types["ATLAS"] += 1 + else: + file_types["INDIVIDUAL"] += 1 + else: + # Perform early return, though this causes undefined behavior in edge cases where one object may have the header property and another may not + col.prop(self, "animateTextures") + if file_types["ATLAS"] == 0: + col.prop(self, "animateTextures") + + col.prop(self, "autoFindMissingTextures") row = self.layout.row() row.prop(self, "useExtraMaps") @@ -392,7 +409,23 @@ class MCPREP_OT_swap_texture_pack( def poll(cls, context): addon_prefs = util.get_user_preferences(context) if addon_prefs.MCprep_exporter_type != "(choose)": - return conf.obj_header.texture_swap_compatible() + if len(context.selected_objects): + file_types = { + "ATLAS" : 0, + "INDIVIDUAL" : 0 + } + for obj in context.selected_objects: + # If the header exists then we should be fine + if obj["MCPREP_OBJ_HEADER"] is not None: + if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": + file_types["ATLAS"] += 1 + else: + file_types["INDIVIDUAL"] += 1 + else: + # Perform early return, though this causes undefined behavior in edge cases where one object may have the header property and another may not + return True + if file_types["ATLAS"] == 0: + return True return False def draw(self, context): diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 65888505..6b11fdc1 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -722,8 +722,13 @@ def draw(self, context): col = split.column(align=True) col.label(text="MCprep tools") col.operator("mcprep.prep_materials", text="Prep Materials") - if not conf.obj_header.texture_swap_compatible(): - col.label(text="OBJ not exported with the correct settings for textureswap") + + if len(context.selected_objects): + for obj in context.selected_objects: + if obj["MCPREP_OBJ_HEADER"] == 1: + if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": + col.label(text="OBJ not exported with the correct settings for textureswap") + break p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path if context.mode == "OBJECT": diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index f1e4ff78..b47c22d3 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -394,8 +394,11 @@ def execute(self, context): prefs = util.get_user_preferences(context) detect_world_exporter(self.filepath) - print(conf.obj_header.exporter()) prefs.MCprep_exporter_type = conf.obj_header.exporter() + + for obj in context.selected_objects: + obj["MCPREP_OBJ_HEADER"] = True + obj["MCPREP_OBJ_FILE_TYPE"] = conf.obj_header.texture_type() if util.bv28(): self.split_world_by_material(context) @@ -403,7 +406,7 @@ def execute(self, context): addon_prefs = util.get_user_preferences(context) self.track_exporter = addon_prefs.MCprep_exporter_type # Soft detect. return {'FINISHED'} - + def obj_name_to_material(self, obj): """Update an objects name based on its first material""" if not obj: From 94c5183c543b611232f16ff8573b96d4cbbf6be1 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 18:06:23 -0600 Subject: [PATCH 044/103] Moved the ObjHeaderOptions class back to world_tools --- MCprep_addon/conf.py | 42 ------------------------------------- MCprep_addon/world_tools.py | 41 +++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/MCprep_addon/conf.py b/MCprep_addon/conf.py index 9b3116ca..c85c9518 100644 --- a/MCprep_addon/conf.py +++ b/MCprep_addon/conf.py @@ -31,45 +31,6 @@ # ADDON GLOBAL VARIABLES AND INITIAL SETTINGS # ----------------------------------------------------------------------------- - -class ObjHeaderOptions: - def __init__(self): - self._exporter = None - self._file_type = None - - """ - Wrapper functions to avoid typos causing issues - """ - def set_mineways(self): - self._exporter = "Mineways" - def set_jmc2obj(self): - self._exporter = "jmc2obj" - - def set_atlas(self): - self._file_type = "ATLAS" - def set_seperated(self): - self._file_type = "INDIVIDUAL_TILES" - - """ - Determines if the OBJ is compatible with textureswap - """ - def texture_swap_compatible(self): - if self._file_type == "INDIVIDUAL_TILES": - return True - return False - - """ - Returns the exporter used - """ - def exporter(self): - return self._exporter if self._exporter is not None else "(choose)" - - """ - Returns the type of textures - """ - def texture_type(self): - return self._file_type if self._file_type is not None else "NONE" - def init(): # ----------------------------------------------- @@ -125,9 +86,6 @@ def init(): global preview_collections preview_collections = {} - global obj_header - obj_header = ObjHeaderOptions() - # ----------------------------------------------- # For initializing the custom icons # ----------------------------------------------- diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index b47c22d3..7c9d18be 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -62,6 +62,37 @@ def get_time_object(): return time_obj_cache +class ObjHeaderOptions: + def __init__(self): + self._exporter = None + self._file_type = None + + """ + Wrapper functions to avoid typos causing issues + """ + def set_mineways(self): + self._exporter = "Mineways" + def set_jmc2obj(self): + self._exporter = "jmc2obj" + + def set_atlas(self): + self._file_type = "ATLAS" + def set_seperated(self): + self._file_type = "INDIVIDUAL_TILES" + + """ + Returns the exporter used + """ + def exporter(self): + return self._exporter if self._exporter is not None else "(choose)" + + """ + Returns the type of textures + """ + def texture_type(self): + return self._file_type if self._file_type is not None else "NONE" + +obj_header = ObjHeaderOptions() def detect_world_exporter(filepath): """Detect whether Mineways or jmc2obj was used, based on prefix info. @@ -83,14 +114,14 @@ def detect_world_exporter(filepath): atlas = re.compile(r'\btextures to three large images|full color texture patterns\b') tiles = re.compile(r'\bexport individual textures|tiles for textures\b') if re.search(atlas, header.lower()) is not None: # If a texture atlas is used - conf.obj_header.set_atlas() + obj_header.set_atlas() elif re.search(tiles, header.lower()) is not None: # If the OBJ uses individual textures - conf.obj_header.set_seperated() + obj_header.set_seperated() return except UnicodeDecodeError: print("failed to read first line of obj: " + filepath) return - conf.obj_header.set_jmc2obj() + obj_header.set_jmc2obj() # ----------------------------------------------------------------------------- # open mineways/jmc2obj related @@ -394,11 +425,11 @@ def execute(self, context): prefs = util.get_user_preferences(context) detect_world_exporter(self.filepath) - prefs.MCprep_exporter_type = conf.obj_header.exporter() + prefs.MCprep_exporter_type = obj_header.exporter() for obj in context.selected_objects: obj["MCPREP_OBJ_HEADER"] = True - obj["MCPREP_OBJ_FILE_TYPE"] = conf.obj_header.texture_type() + obj["MCPREP_OBJ_FILE_TYPE"] = obj_header.texture_type() if util.bv28(): self.split_world_by_material(context) From dc089f55e6751986b42732659b579ece93b9ace2 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 18:06:58 -0600 Subject: [PATCH 045/103] Added a helper class to util.py to handle the checking for compatibility --- MCprep_addon/materials/prep.py | 40 +++++----------------------------- MCprep_addon/util.py | 21 ++++++++++++++++++ 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 10d50f25..d65aa7be 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -130,25 +130,11 @@ def draw_mats_common(self, context): col.prop(self, "usePrincipledShader") col.prop(self, "useReflections") col.prop(self, "makeSolid") - if len(context.selected_objects): - file_types = { - "ATLAS" : 0, - "INDIVIDUAL" : 0 - } - for obj in context.selected_objects: - # If the header exists then we should be fine - if obj["MCPREP_OBJ_HEADER"] is not None: - if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": - file_types["ATLAS"] += 1 - else: - file_types["INDIVIDUAL"] += 1 - else: - # Perform early return, though this causes undefined behavior in edge cases where one object may have the header property and another may not - col.prop(self, "animateTextures") - if file_types["ATLAS"] == 0: - col.prop(self, "animateTextures") + + if util.isTextureSwapCompatible(context): + col.prop(self, "animateTextures") - col.prop(self, "autoFindMissingTextures") + col.prop(self, "autoFindMissingTextures") row = self.layout.row() row.prop(self, "useExtraMaps") @@ -409,23 +395,7 @@ class MCPREP_OT_swap_texture_pack( def poll(cls, context): addon_prefs = util.get_user_preferences(context) if addon_prefs.MCprep_exporter_type != "(choose)": - if len(context.selected_objects): - file_types = { - "ATLAS" : 0, - "INDIVIDUAL" : 0 - } - for obj in context.selected_objects: - # If the header exists then we should be fine - if obj["MCPREP_OBJ_HEADER"] is not None: - if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": - file_types["ATLAS"] += 1 - else: - file_types["INDIVIDUAL"] += 1 - else: - # Perform early return, though this causes undefined behavior in edge cases where one object may have the header property and another may not - return True - if file_types["ATLAS"] == 0: - return True + return util.isTextureSwapCompatible(context) return False def draw(self, context): diff --git a/MCprep_addon/util.py b/MCprep_addon/util.py index 586d5718..0d3513e4 100755 --- a/MCprep_addon/util.py +++ b/MCprep_addon/util.py @@ -220,6 +220,27 @@ def bv30(): return min_bv((3, 00)) +def isTextureSwapCompatible(context): + """Check if the selected objects are textureswap compatible""" + if len(context.selected_objects): + file_types = { + "ATLAS" : 0, + "INDIVIDUAL" : 0 + } + for obj in context.selected_objects: + # If the header exists then we should be fine + if obj["MCPREP_OBJ_HEADER"] is not None: + if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": + file_types["ATLAS"] += 1 + else: + file_types["INDIVIDUAL"] += 1 + else: + # Perform early return, though this causes undefined behavior in edge cases where one object may have the header property and another may not + return True + if file_types["ATLAS"] == 0: + return True + return False + def face_on_edge(faceLoc): """Check if a face is on the boundary between two blocks (local coordinates).""" face_decimals = [loc - loc // 1 for loc in faceLoc] From 74cdf79ae9c414b6febc8ff102e3a485e0f89b30 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 18:10:41 -0600 Subject: [PATCH 046/103] Added a line to make sure Jmc2OBJ works can work with textureswap --- MCprep_addon/world_tools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 7c9d18be..4663f842 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -122,6 +122,7 @@ def detect_world_exporter(filepath): print("failed to read first line of obj: " + filepath) return obj_header.set_jmc2obj() + obj_header.set_seperated() # Since this is the default for Jmc2Obj, we'll assume this is what the OBJ is using # ----------------------------------------------------------------------------- # open mineways/jmc2obj related From 9f1f39d9efa6fb57543bca4f802605618c3c68cb Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 20:17:42 -0600 Subject: [PATCH 047/103] Fixed bug where an improper check occured --- MCprep_addon/mcprep_ui.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 6b11fdc1..7bbe9e8f 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -723,12 +723,8 @@ def draw(self, context): col.label(text="MCprep tools") col.operator("mcprep.prep_materials", text="Prep Materials") - if len(context.selected_objects): - for obj in context.selected_objects: - if obj["MCPREP_OBJ_HEADER"] == 1: - if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": - col.label(text="OBJ not exported with the correct settings for textureswap") - break + if util.isTextureSwapCompatible(context): + col.label(text="OBJ not exported with the correct settings for textureswap") p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path if context.mode == "OBJECT": From fb707e003d5ef0e6b628824017b4f454f76a75c6 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Feb 2023 20:30:34 -0600 Subject: [PATCH 048/103] fixed formatting --- MCprep_addon/materials/prep.py | 1 - MCprep_addon/util.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index d65aa7be..4b04d010 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -133,7 +133,6 @@ def draw_mats_common(self, context): if util.isTextureSwapCompatible(context): col.prop(self, "animateTextures") - col.prop(self, "autoFindMissingTextures") row = self.layout.row() diff --git a/MCprep_addon/util.py b/MCprep_addon/util.py index 0d3513e4..3a82c0a0 100755 --- a/MCprep_addon/util.py +++ b/MCprep_addon/util.py @@ -239,7 +239,8 @@ def isTextureSwapCompatible(context): return True if file_types["ATLAS"] == 0: return True - return False + + return False def face_on_edge(faceLoc): """Check if a face is on the boundary between two blocks (local coordinates).""" From 01246722aec992553669a6ff2535ec99e2f911bf Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 12 Feb 2023 14:49:18 -0600 Subject: [PATCH 049/103] Fixed UI bug due to a missing not keyword --- MCprep_addon/mcprep_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 7bbe9e8f..eaa6fc58 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -723,7 +723,7 @@ def draw(self, context): col.label(text="MCprep tools") col.operator("mcprep.prep_materials", text="Prep Materials") - if util.isTextureSwapCompatible(context): + if not util.isTextureSwapCompatible(context): col.label(text="OBJ not exported with the correct settings for textureswap") p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path From 4ddbb5aff97cb02c1753fe8c6296140d1b0efcc1 Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 12 Feb 2023 14:49:53 -0600 Subject: [PATCH 050/103] Fixed isTextureSwapCompatible function by checking to see if the key exists rather then checking if it's none --- MCprep_addon/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MCprep_addon/util.py b/MCprep_addon/util.py index 3a82c0a0..703d78fd 100755 --- a/MCprep_addon/util.py +++ b/MCprep_addon/util.py @@ -229,7 +229,7 @@ def isTextureSwapCompatible(context): } for obj in context.selected_objects: # If the header exists then we should be fine - if obj["MCPREP_OBJ_HEADER"] is not None: + if "MCPREP_OBJ_HEADER" in obj: if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": file_types["ATLAS"] += 1 else: @@ -239,7 +239,6 @@ def isTextureSwapCompatible(context): return True if file_types["ATLAS"] == 0: return True - return False def face_on_edge(faceLoc): From d79ae9ba40b34e46bf8dc35559b25f4ea5807489 Mon Sep 17 00:00:00 2001 From: mahid Date: Sun, 12 Feb 2023 14:50:39 -0600 Subject: [PATCH 051/103] Removed use of conf.obj_header, removed trailing newlines from header, and removed regex in favor of using tuples --- MCprep_addon/world_tools.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 4663f842..bef7e4e4 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -18,7 +18,6 @@ import os import math -import re import bpy from bpy_extras.io_utils import ExportHelper, ImportHelper @@ -104,18 +103,25 @@ def detect_world_exporter(filepath): try: header = obj_fd.readline() if 'mineways' in header.lower(): - conf.obj_header.set_mineways() + obj_header.set_mineways() # form of: # Wavefront OBJ file made by Mineways version 5.10... for line in obj_fd: if line.startswith("# File type:"): - header = line + header = line.rstrip() # Remove trailing newline # The issue here is that Mineways has changed how the header is generated. As such, we're limited with only a couple of OBJs, some from 2020 and some from 2023, so we'll assume people are using an up to date version. - atlas = re.compile(r'\btextures to three large images|full color texture patterns\b') - tiles = re.compile(r'\bexport individual textures|tiles for textures\b') - if re.search(atlas, header.lower()) is not None: # If a texture atlas is used + atlas = ( + "# File type: Export all textures to three large images", + "# File type: Export full color texture patterns" + ) + tiles = ( + "# File type: Export tiles for textures to directory textures", + "# File type: Export individual textures to directory tex" + ) + print(f"\"{header}\"") + if header in atlas: # If a texture atlas is used obj_header.set_atlas() - elif re.search(tiles, header.lower()) is not None: # If the OBJ uses individual textures + elif header in tiles: # If the OBJ uses individual textures obj_header.set_seperated() return except UnicodeDecodeError: From 5f40b56d76dc20f1a39f8ea30dea58af4b9d820d Mon Sep 17 00:00:00 2001 From: mahid Date: Tue, 14 Feb 2023 21:03:45 -0600 Subject: [PATCH 052/103] Added a docs folder --- docs/common_errors.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/common_errors.md diff --git a/docs/common_errors.md b/docs/common_errors.md new file mode 100644 index 00000000..40187ca9 --- /dev/null +++ b/docs/common_errors.md @@ -0,0 +1,6 @@ +# Common Error messages and what they mean +## OBJ not exported with the correct settings for textureswap +This means the OBJ was exported with incorrect UVs, most commonly UVs for a texture atlas (a giant image with all textures) rather than individual textures. In Mineways, this can be solved by selecting the "Export tiles for textures to directory textures"/"Export individual textures to directory tex" option when exporting, depending on the version. In later versions of Mineways, this should be the default option. + +### Why not simply fix the UVs? +That's a massive pain in the butt to program, not to mention it causes more issues From b9d0c87fd4d70a5d4604fb68a5111f4a46bce265 Mon Sep 17 00:00:00 2001 From: mahid Date: Tue, 14 Feb 2023 21:05:10 -0600 Subject: [PATCH 053/103] Added link to help page --- MCprep_addon/mcprep_ui.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index eaa6fc58..64c4ba1d 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -724,7 +724,11 @@ def draw(self, context): col.operator("mcprep.prep_materials", text="Prep Materials") if not util.isTextureSwapCompatible(context): - col.label(text="OBJ not exported with the correct settings for textureswap") + row = col.row() + row.operator( + "mcprep.open_help", text="", icon="QUESTION", emboss=False + ).url = "https://github.com/StandingPadAnimations/MCprep-Kaion/blob/obj-metadata/docs/common_errors.md#obj-not-exported-with-the-correct-settings-for-textureswap" + row.label(text="OBJ not exported with the correct settings for textureswap") p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path if context.mode == "OBJECT": From 6e77f094ef7362d39f14dbb15be35cd633149190 Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 15 Feb 2023 19:25:14 -0600 Subject: [PATCH 054/103] Fixed bugs that prevented material generation --- MCprep_addon/materials/generate.py | 36 ++++++++++++++++++------------ 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 9bbf4673..a0f23d83 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1501,9 +1501,7 @@ def matgen_cycles_principled( # Sets default transparency value nodeMixTrans.inputs[0].default_value = 1 - nodeFalloff.inputs["Strength"].default_value = 32 - nodeEmitCam.inputs["Strength"].default_value = 4 - + # Sets default reflective values if use_reflections and checklist(canon, "reflective"): principled.inputs["Roughness"].default_value = 0 @@ -1522,21 +1520,25 @@ def matgen_cycles_principled( links.new(nodeTrans.outputs["BSDF"], nodeMixTrans.inputs[1]) links.new(nodeMixTrans.outputs["Shader"], nodeOut.inputs[0]) - if use_emission_nodes: - # Create emission nodes - nodeEmit = create_node( + nodeEmit = create_node( nodes, "ShaderNodeEmission", location=(120, 140)) - nodeEmitCam = create_node( + nodeEmitCam = create_node( nodes, "ShaderNodeEmission", location=(120, 260)) + nodeMixEmit = create_node( + nodes, "ShaderNodeMixShader", location=(420, 0)) + if use_emission_nodes: + # Create emission nodes nodeMixCam = create_node( nodes, "ShaderNodeMixShader", location=(320, 260)) nodeFalloff = create_node( nodes, "ShaderNodeLightFalloff", location=(-80, 320)) nodeLightPath = create_node( nodes, "ShaderNodeLightPath", location=(-320, 520)) - nodeMixEmit = create_node( - nodes, "ShaderNodeMixShader", location=(420, 0)) - + + # Set values + nodeFalloff.inputs["Strength"].default_value = 32 + nodeEmitCam.inputs["Strength"].default_value = 4 + # Create the links links.new(principled.outputs["BSDF"], nodeMixEmit.inputs[1]) links.new(nodeLightPath.outputs["Is Camera Ray"], nodeMixCam.inputs["Fac"]) @@ -1550,7 +1552,8 @@ def matgen_cycles_principled( nodeMixEmit.inputs[0].default_value = 1 else: nodeMixEmit.inputs[0].default_value = 0 - + else: + links.new(principled.outputs[0], nodeMixTrans.inputs[2]) nodeInputs = [ @@ -1564,9 +1567,12 @@ def matgen_cycles_principled( [principled.inputs["Metallic"]], [principled.inputs["Specular"]], [principled.inputs["Normal"]]] - + + if not use_emission_nodes: + nodes.remove(nodeEmit) + nodes.remove(nodeEmitCam) + nodes.remove(nodeMixEmit) # generate texture format and connect - if pack_format == "specular": texgen_specular(mat, passes, nodeInputs, use_reflections) elif pack_format == "seus": @@ -1576,7 +1582,9 @@ def matgen_cycles_principled( nodes.remove(nodeTrans) nodes.remove(nodeMixTrans) nodeOut.location = (620, 0) - links.new(nodeMixEmit.outputs[0], nodeOut.inputs[0]) + + if use_emission_nodes: + links.new(nodeMixEmit.outputs[0], nodeOut.inputs[0]) # faster, and appropriate for non-transparent (and refelctive?) materials principled.distribution = 'GGX' From e582e6d562b5abd38223a323f443b59b7cb52bd7 Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 15 Feb 2023 19:31:59 -0600 Subject: [PATCH 055/103] Added suggestion made by TheDuckCow --- MCprep_addon/materials/generate.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index a0f23d83..5d54ca34 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1428,14 +1428,13 @@ def matgen_cycles_simple( if hasattr(mat, "shadow_method"): mat.shadow_method = 'HASHED' - if use_emission_nodes: - if use_emission: - inputs = [inp.name for inp in principled.inputs] - if 'Emission Strength' in inputs: # Later 2.9 versions only. - principled.inputs['Emission Strength'].default_value = 1 - links.new( - nodeSaturateMix.outputs[saturateMixOut[0]], - principled.inputs["Emission"]) + if use_emission_nodes and use_emission: + inputs = [inp.name for inp in principled.inputs] + if 'Emission Strength' in inputs: # Later 2.9 versions only. + principled.inputs['Emission Strength'].default_value = 1 + links.new( + nodeSaturateMix.outputs[saturateMixOut[0]], + principled.inputs["Emission"]) # reapply animation data if any to generated nodes apply_texture_animation_pass_settings(mat, animated_data) From a0332751df9047bf7afaac3cee8c84debac176ba Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 15 Feb 2023 19:44:32 -0600 Subject: [PATCH 056/103] Made the option only appear in Cycles and always be false in EEVEE --- MCprep_addon/materials/prep.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index ca32a8b9..370bcb28 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -51,7 +51,7 @@ def pack_formats(self, context): itms.append(("specular", "Specular", "Sets the pack format to Specular.")) itms.append(("seus", "SEUS", "Sets the pack format to SEUS.")) return itms - + animateTextures = bpy.props.BoolProperty( name="Animate textures (may be slow first time)", description=( @@ -154,8 +154,13 @@ def draw_mats_common(self, context): col.prop(self, "combineMaterials") row = self.layout.row() row.prop(self, "optimizeScene") - row.prop(self, "useEmission") + # EEVEE won't benefit from this anyway, so best to disable it + if engine == 'CYCLES': + self.useEmission = True + row.prop(self, "useEmission") + else: + self.useEmission = False class MCPREP_OT_prep_materials(bpy.types.Operator, McprepMaterialProps): """Fixes materials and textures on selected objects for Minecraft rendering""" From 409919a71db3943d79bcfe191258c8f151527bab Mon Sep 17 00:00:00 2001 From: mahid Date: Mon, 20 Feb 2023 15:47:29 -0600 Subject: [PATCH 057/103] Fixed some generation issues --- MCprep_addon/materials/generate.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 5d54ca34..4ba1f3db 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -1199,7 +1199,7 @@ def texgen_specular(mat, passes, nodeInputs, use_reflections): nodeTexDiff.image = image_diff -def texgen_seus(mat, passes, nodeInputs, use_reflections): +def texgen_seus(mat, passes, nodeInputs, use_reflections, use_emission): matGen = util.nameGeneralize(mat.name) canon, form = get_mc_canonical_name(matGen) @@ -1276,12 +1276,15 @@ def texgen_seus(mat, passes, nodeInputs, use_reflections): links.new(nodeSeperate.outputs["R"], nodeSpecInv.inputs["Color"]) for i in nodeInputs[0]: + if i == nodeSaturateMix.outputs[saturateMixOut[0]]: + continue links.new(nodeSaturateMix.outputs[saturateMixOut[0]], i) for i in nodeInputs[1]: links.new(nodeTexDiff.outputs["Alpha"], i) if image_spec and use_reflections: - for i in nodeInputs[2]: - links.new(nodeSeperate.outputs["B"], i) + if use_emission: + for i in nodeInputs[2]: + links.new(nodeSeperate.outputs["B"], i) for i in nodeInputs[4]: links.new(nodeSeperate.outputs["G"], i) for i in nodeInputs[3]: @@ -1575,7 +1578,7 @@ def matgen_cycles_principled( if pack_format == "specular": texgen_specular(mat, passes, nodeInputs, use_reflections) elif pack_format == "seus": - texgen_seus(mat, passes, nodeInputs, use_reflections) + texgen_seus(mat, passes, nodeInputs, use_reflections, use_emission_nodes) if only_solid is True or checklist(canon, "solid"): nodes.remove(nodeTrans) @@ -1805,7 +1808,7 @@ def matgen_cycles_original( if pack_format == "specular": texgen_specular(mat, passes, nodeInputs, use_reflections) elif pack_format == "seus": - texgen_seus(mat, passes, nodeInputs, use_reflections) + texgen_seus(mat, passes, nodeInputs, use_reflections, use_emission_nodes) if only_solid is True or checklist(canon, "solid"): nodes.remove(nodeTrans) From 169de343c9db51026537ae518d5f9887208e2e85 Mon Sep 17 00:00:00 2001 From: mahid Date: Tue, 21 Feb 2023 12:35:51 -0600 Subject: [PATCH 058/103] Fixed indexing for sat_node.inputs --- MCprep_addon/materials/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 85966bc7..84fd3686 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -954,7 +954,7 @@ def set_saturation_material(mat): return # requires regenerating material to add back if len(desat_color) == 3: desat_color += [1] # add in alpha - sat_node.inputs[2].default_value = desat_color + sat_node.inputs[6].default_value = desat_color sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) From ed924bf9e531d120a65ebec77a390915f39c110c Mon Sep 17 00:00:00 2001 From: mahid Date: Tue, 21 Feb 2023 12:40:09 -0600 Subject: [PATCH 059/103] Added version check --- MCprep_addon/materials/generate.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 84fd3686..f97267b2 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -954,7 +954,11 @@ def set_saturation_material(mat): return # requires regenerating material to add back if len(desat_color) == 3: desat_color += [1] # add in alpha - sat_node.inputs[6].default_value = desat_color + + if util.min_bv((3, 4)): + sat_node.inputs[6].default_value = desat_color + else: + sat_node.inputs[2].default_value = desat_color sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) From b35246f0aaeaa4fafa6979c61af42d43b58e6e9f Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 22 Feb 2023 18:20:25 -0600 Subject: [PATCH 060/103] Replaced node indexing based on zNight's recommendation on Discord --- MCprep_addon/materials/generate.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index f97267b2..58d5155d 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -955,10 +955,8 @@ def set_saturation_material(mat): if len(desat_color) == 3: desat_color += [1] # add in alpha - if util.min_bv((3, 4)): - sat_node.inputs[6].default_value = desat_color - else: - sat_node.inputs[2].default_value = desat_color + sat_node_in = get_node_socket(node, is_input=True) # Get the node sockets in a version agnostic way + sat_node.inputs[sat_node_in[2]].default_value = desat_color sat_node.mute = not bool(saturate) sat_node.hide = not bool(saturate) From 6c1988098422f118f9626a89e45fec906fb4c599 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Fri, 24 Feb 2023 22:48:14 -0800 Subject: [PATCH 061/103] Fix generation references and improve readability --- MCprep_addon/materials/prep.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 370bcb28..22c577d0 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -249,8 +249,13 @@ def execute(self, context): count += 1 elif engine == 'CYCLES' or engine == 'BLENDER_EEVEE': res = generate.matprep_cycles( - mat, passes, self.useReflections, - self.usePrincipledShader, self.makeSolid, self.packFormat, self.useEmission) + mat=mat, + passes=passes, + use_reflections=self.useReflections, + use_principled=self.usePrincipledShader, + only_solid=self.makeSolid, + pack_format=self.packFormat, + use_emission_nodes=self.useEmission) if res == 0: count += 1 else: @@ -631,8 +636,13 @@ def update_material(self, context, mat): mat, passes, self.useReflections, self.makeSolid) elif engine == 'CYCLES' or engine == 'BLENDER_EEVEE': res = generate.matprep_cycles( - mat, passes, self.useReflections, - self.usePrincipledShader, self.makeSolid, self.packFormat) + mat=mat, + passes=passes, + use_reflections=self.useReflections, + use_principled=self.usePrincipledShader, + only_solid=self.makeSolid, + pack_format=self.packFormat, + use_emission_nodes=self.useEmission) else: return False, "Only Blender Internal, Cycles, or Eevee supported" From f471506657b1ca7beff9157f85f986ce1104664c Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Mon, 6 Mar 2023 10:03:06 -0500 Subject: [PATCH 062/103] Set default particle planes folder and fixed emitter material With this change unfortunately it does not remember your selection on next press, as we are assigning in the UI. There may be a better way to assert the value in property settings, or by having a secondary scene 'last particle plane' selection variable which is passed in. --- MCprep_addon/mcprep_ui.py | 13 +++++++++++++ MCprep_addon/spawner/effects.py | 18 +++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 3e4640d9..559b50ef 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -1546,6 +1546,19 @@ def effects_spawner(self, context): ops.location = util.get_cuser_location(context) ops.frame = context.scene.frame_current + initial_sel = os.path.join( + context.scene.mcprep_texturepack_path, + "assets", "minecraft", "textures", "block", "dirt.png") + alt_sel = os.path.join( + context.scene.mcprep_texturepack_path, + "assets", "minecraft", "textures", "block") + if os.path.isfile(initial_sel): + ops.filepath = initial_sel + elif os.path.isdir(alt_sel): + ops.filepath = alt_sel + else: + ops.filepath = context.scene.mcprep_texturepack_path + col = layout.column() if not scn_props.show_settings_effect: col.prop( diff --git a/MCprep_addon/spawner/effects.py b/MCprep_addon/spawner/effects.py index a8110986..daac7755 100644 --- a/MCprep_addon/spawner/effects.py +++ b/MCprep_addon/spawner/effects.py @@ -372,6 +372,22 @@ def add_particle_planes_effect(context, image_path, location, frame): pcoll = get_or_create_particle_meshes_coll( context, f_name, img) + # Set the material for the emitter object. Though not actually rendered, + # this helps clarify which particle is being emitted from this object. + mat = None + for ob in pcoll.objects: + if ob.material_slots and ob.material_slots[0].material: + mat = ob.material_slots[0].material + break + if mat: + if not obj.material_slots: + obj.data.materials.append(mat) + # Must use 'OBEJCT' instead of mesh data, otherwise all particle + # emitters will have the same material (how it was initially working) + obj.material_slots[0].link = 'OBJECT' + obj.material_slots[0].material = mat + print("Linked {} with {}".format(obj.name, mat.name)) + apply_particle_settings(obj, frame, base_name, pcoll) @@ -600,7 +616,7 @@ def apply_particle_settings(obj, frame, base_name, pcoll): psystem.settings.lifetime_random = 0.2 psystem.settings.emit_from = 'FACE' psystem.settings.distribution = 'RAND' - psystem.settings.normal_factor = -1.5 + psystem.settings.normal_factor = 1.5 psystem.settings.use_rotations = True psystem.settings.rotation_factor_random = 1 psystem.settings.particle_size = 0.2 From 5d0e42185919345a92631c5964f5562ac4fbd06b Mon Sep 17 00:00:00 2001 From: mahid Date: Mon, 6 Mar 2023 19:54:03 -0600 Subject: [PATCH 063/103] Added the ability to "import" MTL files When MCprep opens the file picker, both OBJs and MTLs can be selected, which was annoying when accidently selecting the MTL file of the OBJ. This adds a quality of life improvement by replacing .mtl with .obj if the user accidently clicks the MTL file --- MCprep_addon/world_tools.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index bef7e4e4..94eb5c01 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -18,6 +18,7 @@ import os import math +from pathlib import Path import bpy from bpy_extras.io_utils import ExportHelper, ImportHelper @@ -316,15 +317,16 @@ class MCPREP_OT_import_world_split(bpy.types.Operator, ImportHelper): @tracking.report_error def execute(self, context): # for consistency with the built in one, only import the active path + if self.filepath.lower().endswith(".mtl"): + filename = Path(self.filepath) + new_filename = filename.with_suffix(".obj") + self.filepath = str(new_filename) # Change it from MTL to OBJ, this will be checked with the rest of the if clauses if not self.filepath: self.report({"ERROR"}, "File not found, could not import obj") return {'CANCELLED'} if not os.path.isfile(self.filepath): self.report({"ERROR"}, "File not found, could not import obj") return {'CANCELLED'} - if self.filepath.lower().endswith(".mtl"): - self.report({"ERROR"}, "Select the .obj file, NOT the .mtl!") - return {'CANCELLED'} if not self.filepath.lower().endswith(".obj"): self.report({"ERROR"}, "You must select a .obj file to import") return {'CANCELLED'} From f541063fd46959df1422dccc3d3767ffbecc07ae Mon Sep 17 00:00:00 2001 From: mahid Date: Mon, 6 Mar 2023 20:24:15 -0600 Subject: [PATCH 064/103] Added some guides on commits Recently I found a nice guide on writing git commits, and I think we should encourage users to start writing git commits as an explanation for why changes were made. --- CONTRIBUTING.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 98ae96f1..abf36390 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -171,6 +171,25 @@ One other detail: MCprep uses git lfs or Large File Storage, to avoid saving bin Run into other gotchas? Please open a [new issue](https://github.com/TheDuckCow/MCprep/issues)! + +## Commit Messages +Git commits should explain why a change was made, because the diff will show the changes made. For example, instead of writing: +``` +Added ability to "import" MTL files +``` + +Instead do: +``` +Added the ability to "import" MTL files + +MCprep's file explorer shows both OBJs and MTLs, and sometimes users end up clicking +MTL files. This brings a quality of life improvement to change the extension +if the file selected is an MTL, since MTLs share the same name as their corresponding +OBJ files +``` + +The first line is a summary, and should be less then 50 characters. + ## IDE Support If you're using an IDE, it's recommened to install `bpy` as a Python module. In my (StandingPad) experiance, the [fake-bpy package](https://github.com/nutti/fake-bpy-module) seems to be the best. From bc38e6d6e75e95c8e9d8df765160d006c1f22a41 Mon Sep 17 00:00:00 2001 From: mahid Date: Mon, 6 Mar 2023 20:33:18 -0600 Subject: [PATCH 065/103] Added a template to make it easier for users This template includes some reference for how long each line should be. It comes from this Gist (modified to reduce length): https://gist.github.com/lisawolderiksen/a7b99d94c92c6671181611be1641c733 --- CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index abf36390..ae42d196 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -190,6 +190,20 @@ OBJ files The first line is a summary, and should be less then 50 characters. +Git won't automatically wrap messages either, so each line should have a limit of 72 characters. + +Here's a template I found that can help (modified for simplicity): +``` +# Title: Summary, imperative, start upper case, don't end with a period +# No more than 50 chars. #### 50 chars is here: # + +# Body: Explain *what* and *why* (not *how*). Include task ID (Jira issue). +# Wrap at 72 chars. ################################## which is here: # + +``` +Add this to a file called .gitmessage, and then execute the following command: +`git config --global commit.template /path/to/.gitmessage` + ## IDE Support If you're using an IDE, it's recommened to install `bpy` as a Python module. In my (StandingPad) experiance, the [fake-bpy package](https://github.com/nutti/fake-bpy-module) seems to be the best. From e34530adcb0ab88097092948aab3b5af69989be5 Mon Sep 17 00:00:00 2001 From: mahid Date: Mon, 6 Mar 2023 23:16:34 -0600 Subject: [PATCH 066/103] Added some extra details Explained how Git commits should justify the changes, and added some details on verbose commits and setting them up. --- CONTRIBUTING.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ae42d196..91d4bc2b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -188,11 +188,11 @@ if the file selected is an MTL, since MTLs share the same name as their correspo OBJ files ``` -The first line is a summary, and should be less then 50 characters. +The first line is a summary of the changes, and should be less then 50 characters. The rest should justify the changes. Convince us why these changes are important and why they've been made this way. Git won't automatically wrap messages either, so each line should have a limit of 72 characters. -Here's a template I found that can help (modified for simplicity): +Here's a template I found that can help (modified for simplicity) by using # to define which is the limit Git can display for each line: ``` # Title: Summary, imperative, start upper case, don't end with a period # No more than 50 chars. #### 50 chars is here: # @@ -202,7 +202,9 @@ Here's a template I found that can help (modified for simplicity): ``` Add this to a file called .gitmessage, and then execute the following command: -`git config --global commit.template /path/to/.gitmessage` +`git config --local commit.template /path/to/.gitmessage` + +To use for each commit, you can use `git config --local commit.verbose true` to tell Git to perform a verbose commit all the time for just the MCprep repo. ## IDE Support If you're using an IDE, it's recommened to install `bpy` as a Python module. In my (StandingPad) experiance, the [fake-bpy package](https://github.com/nutti/fake-bpy-module) seems to be the best. From 4cf67c717cada36af6c66a6baf607a9a0e2058ae Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Wed, 8 Mar 2023 09:25:26 -0500 Subject: [PATCH 067/103] Last particle planes file is now remembered. Added scene variable `mcprep_particle_plane_file` which is initially unset, only assigned after a confirmed, non-cancelled particle plane effect placement. --- MCprep_addon/materials/generate.py | 4 ++++ MCprep_addon/mcprep_ui.py | 37 +++++++++++++++++++++--------- MCprep_addon/spawner/effects.py | 2 ++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 85966bc7..c7cda9af 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -35,6 +35,10 @@ def update_mcprep_texturepack_path(self, context): bpy.ops.mcprep.reload_models() conf.material_sync_cache = None + # Forces particle plane emitter to now use the newly set resource pack + # the first time, but the value gets saved again after. + context.scene.mcprep_particle_plane_file = '' + def get_mc_canonical_name(name): """Convert a material name to standard MC name. diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 559b50ef..84d53260 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -1546,18 +1546,23 @@ def effects_spawner(self, context): ops.location = util.get_cuser_location(context) ops.frame = context.scene.frame_current - initial_sel = os.path.join( - context.scene.mcprep_texturepack_path, - "assets", "minecraft", "textures", "block", "dirt.png") - alt_sel = os.path.join( - context.scene.mcprep_texturepack_path, - "assets", "minecraft", "textures", "block") - if os.path.isfile(initial_sel): - ops.filepath = initial_sel - elif os.path.isdir(alt_sel): - ops.filepath = alt_sel + # If particle planes has not been changed yet this session, + # use the texture pack location in + if not context.scene.mcprep_particle_plane_file: + initial_sel = os.path.join( + context.scene.mcprep_texturepack_path, + "assets", "minecraft", "textures", "block", "dirt.png") + alt_sel = os.path.join( + context.scene.mcprep_texturepack_path, + "assets", "minecraft", "textures", "block") + if os.path.isfile(initial_sel): + ops.filepath = initial_sel + elif os.path.isdir(alt_sel): + ops.filepath = alt_sel + else: + ops.filepath = context.scene.mcprep_texturepack_path else: - ops.filepath = context.scene.mcprep_texturepack_path + ops.filepath = context.scene.mcprep_particle_plane_file col = layout.column() if not scn_props.show_settings_effect: @@ -1576,6 +1581,11 @@ def effects_spawner(self, context): subrow.prop(context.scene, "mcprep_effects_path", text="") subrow.operator("mcprep.effects_path_reset", icon=LOAD_FACTORY, text="") + box.label(text="Texture pack folder") + row = box.row(align=True) + row.prop(context.scene, "mcprep_texturepack_path", text="") + row.operator("mcprep.reset_texture_path", text="", icon=LOAD_FACTORY) + base = bpy.path.abspath(context.scene.mcprep_effects_path) if not os.path.isdir(base): b_col.label(text="Effects folder not found", icon="ERROR") @@ -1953,6 +1963,11 @@ def register(): subtype='DIR_PATH', update=effects.update_effects_path, default=addon_prefs.effects_path) + bpy.types.Scene.mcprep_particle_plane_file = bpy.props.StringProperty( + name="Particle planes file set", + description="Has the particle planes file been changed", + subtype='FILE_PATH', + default='') bpy.types.Scene.entity_path = bpy.props.StringProperty( name="Entity file", description="File for entity library", diff --git a/MCprep_addon/spawner/effects.py b/MCprep_addon/spawner/effects.py index daac7755..78479cd3 100644 --- a/MCprep_addon/spawner/effects.py +++ b/MCprep_addon/spawner/effects.py @@ -1196,6 +1196,8 @@ def execute(self, context): bpy.ops.mcprep.prompt_reset_spawners('INVOKE_DEFAULT') return {'CANCELLED'} + context.scene.mcprep_particle_plane_file = self.filepath + self.track_param = name return {'FINISHED'} From 98687b37b6bbf85219fbd538c7982dfbed9eee82 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Wed, 8 Mar 2023 09:39:57 -0500 Subject: [PATCH 068/103] Reverting disable emission with eevee. Eevee still benefits from direct to camera shading as emitter, plus some people may use probes anyways. Also this fixes the redo-last for use emission, as currently the draw function immediately turns it back on once you've tried to uncheck it. --- MCprep_addon/materials/prep.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 22c577d0..cf6ccb27 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -154,13 +154,8 @@ def draw_mats_common(self, context): col.prop(self, "combineMaterials") row = self.layout.row() row.prop(self, "optimizeScene") + row.prop(self, "useEmission") - # EEVEE won't benefit from this anyway, so best to disable it - if engine == 'CYCLES': - self.useEmission = True - row.prop(self, "useEmission") - else: - self.useEmission = False class MCPREP_OT_prep_materials(bpy.types.Operator, McprepMaterialProps): """Fixes materials and textures on selected objects for Minecraft rendering""" From 53cd867cd22d24cd2c381058ae6ed1a70cc77a63 Mon Sep 17 00:00:00 2001 From: StandingPad Date: Wed, 8 Mar 2023 17:09:48 +0000 Subject: [PATCH 069/103] Update CONTRIBUTING.md Co-authored-by: Patrick W. Crawford --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 91d4bc2b..5b29bdf5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -192,7 +192,7 @@ The first line is a summary of the changes, and should be less then 50 character Git won't automatically wrap messages either, so each line should have a limit of 72 characters. -Here's a template I found that can help (modified for simplicity) by using # to define which is the limit Git can display for each line: +Here's a template some MCprep developers found that can help (modified for simplicity) by using # to define which is the limit Git can display for each line: ``` # Title: Summary, imperative, start upper case, don't end with a period # No more than 50 chars. #### 50 chars is here: # From 2e30ecd1f5d1b6b7d761a23492dcaf293819d5ae Mon Sep 17 00:00:00 2001 From: mahid Date: Tue, 14 Feb 2023 21:29:15 -0600 Subject: [PATCH 070/103] MCprep now uses C++ importer in Blender 3.1 and above --- MCprep_addon/world_tools.py | 49 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 88ba51cf..35a1fce4 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -310,8 +310,13 @@ def execute(self, context): obj_import_mem_msg = ( "Memory error during OBJ import, try exporting a smaller world") try: - res = bpy.ops.import_scene.obj( - filepath=self.filepath, use_split_groups=True) + res = None + if util.min_bv((3, 1)): + res = bpy.ops.wm.obj_import( + filepath=self.filepath) + else: + res = bpy.ops.wm.obj_import( + filepath=self.filepath, use_split_groups=True) except MemoryError as err: print("Memory error during import OBJ:") print(err) @@ -863,34 +868,34 @@ def create_dynamic_world(self, context, blendfile, wname): # # update drivers, needed if time has changed vs import source # if context.scene.world.node_tree.animation_data: - # # context.scene.world.node_tree.animation_data.drivers[0].update() - # drivers = context.scene.world.node_tree.animation_data.drivers[0] - # drivers.driver.variables[0].targets[0].id = time_obj - # # nope, still doesn't work. + # # context.scene.world.node_tree.animation_data.drivers[0].update() + # drivers = context.scene.world.node_tree.animation_data.drivers[0] + # drivers.driver.variables[0].targets[0].id = time_obj + # # nope, still doesn't work. # if needed: create time object and setup drivers # if not time_obj: - # conf.log("Creating time_obj") - # time_obj = bpy.data.objects.new('MCprep Time Control', None) - # util.obj_link_scene(time_obj, context) - # global time_obj_cache - # time_obj_cache = time_obj - # if hasattr(time_obj, "empty_draw_type"): # 2.7 - # time_obj.empty_draw_type = 'SPHERE' - # else: # 2.8 - # time_obj.empty_display_type = 'SPHERE' + # conf.log("Creating time_obj") + # time_obj = bpy.data.objects.new('MCprep Time Control', None) + # util.obj_link_scene(time_obj, context) + # global time_obj_cache + # time_obj_cache = time_obj + # if hasattr(time_obj, "empty_draw_type"): # 2.7 + # time_obj.empty_draw_type = 'SPHERE' + # else: # 2.8 + # time_obj.empty_display_type = 'SPHERE' # first, get the driver # if (not world.node_tree.animation_data - # or not world.node_tree.animation_data.drivers - # or not world.node_tree.animation_data.drivers[0].driver): - # conf.log("Could not get driver from imported dynamic world") - # self.report({'WARNING'}, "Could not update driver for dynamic world") - # driver = None + # or not world.node_tree.animation_data.drivers + # or not world.node_tree.animation_data.drivers[0].driver): + # conf.log("Could not get driver from imported dynamic world") + # self.report({'WARNING'}, "Could not update driver for dynamic world") + # driver = None # else: - # driver = world.node_tree.animation_data.drivers[0].driver + # driver = world.node_tree.animation_data.drivers[0].driver # if driver and driver.variables[0].targets[0].id_type == 'OBJECT': - # driver.variables[0].targets[0].id = time_obj + # driver.variables[0].targets[0].id = time_obj # add driver to control obj's x rotation return obj_list From 8d2e5e475e590a668f3cd5e65dfe44ca3dcd7eef Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 15 Feb 2023 18:04:45 -0600 Subject: [PATCH 071/103] Added check for 3.5, which while in alpha will bring back use_split_groups for the C++ OBJ importer --- MCprep_addon/world_tools.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 35a1fce4..5e4338be 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -311,6 +311,9 @@ def execute(self, context): "Memory error during OBJ import, try exporting a smaller world") try: res = None + if util.min_bv((3, 5)): + res = bpy.ops.wm.obj_import( + filepath=self.filepath, use_split_groups=True) # Returns functionality missing in 3.1 - 3.4 if util.min_bv((3, 1)): res = bpy.ops.wm.obj_import( filepath=self.filepath) From 3d1451c9cdea3e13c56e21351dbde1ff03f24df0 Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 22 Feb 2023 18:12:45 -0600 Subject: [PATCH 072/103] C++ now only supported for 3.5 for full feature parity --- MCprep_addon/world_tools.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 5e4338be..92e16a99 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -313,10 +313,7 @@ def execute(self, context): res = None if util.min_bv((3, 5)): res = bpy.ops.wm.obj_import( - filepath=self.filepath, use_split_groups=True) # Returns functionality missing in 3.1 - 3.4 - if util.min_bv((3, 1)): - res = bpy.ops.wm.obj_import( - filepath=self.filepath) + filepath=self.filepath, use_split_groups=True) else: res = bpy.ops.wm.obj_import( filepath=self.filepath, use_split_groups=True) From 9f4ad3b72057a171a7d03c47c67913341a91f285 Mon Sep 17 00:00:00 2001 From: mahid Date: Mon, 6 Mar 2023 19:54:03 -0600 Subject: [PATCH 073/103] Added the ability to "import" MTL files When MCprep opens the file picker, both OBJs and MTLs can be selected, which was annoying when accidently selecting the MTL file of the OBJ. This adds a quality of life improvement by replacing .mtl with .obj if the user accidently clicks the MTL file --- MCprep_addon/world_tools.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 92e16a99..ad7c497c 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -18,6 +18,7 @@ import os import math +from pathlib import Path import bpy from bpy_extras.io_utils import ExportHelper, ImportHelper @@ -272,15 +273,16 @@ class MCPREP_OT_import_world_split(bpy.types.Operator, ImportHelper): @tracking.report_error def execute(self, context): # for consistency with the built in one, only import the active path + if self.filepath.lower().endswith(".mtl"): + filename = Path(self.filepath) + new_filename = filename.with_suffix(".obj") + self.filepath = str(new_filename) # Change it from MTL to OBJ, this will be checked with the rest of the if clauses if not self.filepath: self.report({"ERROR"}, "File not found, could not import obj") return {'CANCELLED'} if not os.path.isfile(self.filepath): self.report({"ERROR"}, "File not found, could not import obj") return {'CANCELLED'} - if self.filepath.lower().endswith(".mtl"): - self.report({"ERROR"}, "Select the .obj file, NOT the .mtl!") - return {'CANCELLED'} if not self.filepath.lower().endswith(".obj"): self.report({"ERROR"}, "You must select a .obj file to import") return {'CANCELLED'} From b6ba11bbd528dd7d3ad830573e70a6660ef8a6b9 Mon Sep 17 00:00:00 2001 From: mahid Date: Wed, 8 Mar 2023 16:48:39 -0600 Subject: [PATCH 074/103] Implemented MTL conversion for ACES and whatnot ACES and other colorspaces like it have issues when it comes to OBJ importing, so we need to convert the MTL so that Blender doesn't throw an exception when importing. This comments out lines starting `map_d` from the MTL. The operation itself shouldn't affect MCprep since we don't deal with it anyway. Of course the backup needs to be fixed (right now it will always back up the file, so we could add a timestamp to the edited MTL that MCprep then checks for and compares before attempting to convert) --- MCprep_addon/world_tools.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 88ba51cf..29ac0bcd 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -18,6 +18,8 @@ import os import math +from pathlib import Path +import shutil import bpy from bpy_extras.io_utils import ExportHelper, ImportHelper @@ -310,6 +312,35 @@ def execute(self, context): obj_import_mem_msg = ( "Memory error during OBJ import, try exporting a smaller world") try: + try: + BLENDER_STANDARD = ( + "Standard", + "Filmic", + "Filmic Log", + "Raw", + "False Color" + ) + MTL = self.filepath.rsplit(".", 1)[0] + '.mtl' + LINES = None + if bpy.context.scene.view_settings.view_transform not in BLENDER_STANDARD: + # This represents a new folder that'll backup the MTL file + original_mtl_path = Path(self.filepath).parent.absolute() / "ORIGINAL_MTLS" # Pathlib is weird when it comes to appending + + # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but we should confirm nonetheless + original_mtl_path.mkdir(parents=True, exist_ok=True) + shutil.copy2(MTL, original_mtl_path.absolute()) # Copy the MTL with metadata + + # Open the MTL + with open(MTL, 'r') as mtl_file: + LINES = mtl_file.readlines() + for index, line in enumerate(LINES): + if line.startswith("map_d"): + LINES[index] = "# " + line + with open(MTL, 'w') as mtl_file: + mtl_file.writelines(LINES) + + except Exception: + self.report({"ERROR"}, "Failed to convert MTL for compatbility") res = bpy.ops.import_scene.obj( filepath=self.filepath, use_split_groups=True) except MemoryError as err: From ae3ff041b7b033c074a7ac504ff944111a132dc0 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Thu, 9 Mar 2023 09:56:07 -0500 Subject: [PATCH 075/103] Renamed isTextureSwapCompatible to is_atlas_export Performed some additional code readability and streamlining changes, and made the row always show up but display an alt message if non compatible world import present. --- MCprep_addon/materials/prep.py | 13 +++++++--- MCprep_addon/mcprep_ui.py | 4 +-- MCprep_addon/util.py | 47 ++++++++++++++++++++-------------- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py index 4b04d010..ee709350 100755 --- a/MCprep_addon/materials/prep.py +++ b/MCprep_addon/materials/prep.py @@ -130,9 +130,14 @@ def draw_mats_common(self, context): col.prop(self, "usePrincipledShader") col.prop(self, "useReflections") col.prop(self, "makeSolid") - - if util.isTextureSwapCompatible(context): - col.prop(self, "animateTextures") + + anim_row = col.row() + can_swap_text = util.is_atlas_export(context) + anim_row.enabled = can_swap_text + if can_swap_text: + anim_row.prop(self, "animateTextures") + else: + anim_row.prop(self, "animateTextures", text="Animate textures (disabled due to import settings)") col.prop(self, "autoFindMissingTextures") row = self.layout.row() @@ -394,7 +399,7 @@ class MCPREP_OT_swap_texture_pack( def poll(cls, context): addon_prefs = util.get_user_preferences(context) if addon_prefs.MCprep_exporter_type != "(choose)": - return util.isTextureSwapCompatible(context) + return util.is_atlas_export(context) return False def draw(self, context): diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 64c4ba1d..290b54c6 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -723,12 +723,12 @@ def draw(self, context): col.label(text="MCprep tools") col.operator("mcprep.prep_materials", text="Prep Materials") - if not util.isTextureSwapCompatible(context): + if not util.is_atlas_export(context): row = col.row() row.operator( "mcprep.open_help", text="", icon="QUESTION", emboss=False ).url = "https://github.com/StandingPadAnimations/MCprep-Kaion/blob/obj-metadata/docs/common_errors.md#obj-not-exported-with-the-correct-settings-for-textureswap" - row.label(text="OBJ not exported with the correct settings for textureswap") + row.label(text="OBJ incompatible with textureswap") p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path if context.mode == "OBJECT": diff --git a/MCprep_addon/util.py b/MCprep_addon/util.py index 703d78fd..c218eab1 100755 --- a/MCprep_addon/util.py +++ b/MCprep_addon/util.py @@ -220,26 +220,35 @@ def bv30(): return min_bv((3, 00)) -def isTextureSwapCompatible(context): - """Check if the selected objects are textureswap compatible""" - if len(context.selected_objects): - file_types = { - "ATLAS" : 0, - "INDIVIDUAL" : 0 - } - for obj in context.selected_objects: - # If the header exists then we should be fine - if "MCPREP_OBJ_HEADER" in obj: - if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": - file_types["ATLAS"] += 1 - else: - file_types["INDIVIDUAL"] += 1 +def is_atlas_export(context): + """Check if the selected objects are textureswap/animate tex compatible. + + Atlas textures are ones where all textures are combined into a single file, + while individual textures is where there is one image file per block type. + + Returns a bool. If false, the UI will show a warning and link to doc + about using the right settings. + """ + if not context.selected_objects: + # Don't trigger the UI warning just because nothing is selected + return True + + file_types = { + "ATLAS": 0, + "INDIVIDUAL": 0 + } + for obj in context.selected_objects: + # If the header exists then we should be fine + if "MCPREP_OBJ_HEADER" in obj: + if obj["MCPREP_OBJ_FILE_TYPE"] == "ATLAS": + file_types["ATLAS"] += 1 else: - # Perform early return, though this causes undefined behavior in edge cases where one object may have the header property and another may not - return True - if file_types["ATLAS"] == 0: - return True - return False + file_types["INDIVIDUAL"] += 1 + else: + continue + + return file_types["ATLAS"] == 0 + def face_on_edge(faceLoc): """Check if a face is on the boundary between two blocks (local coordinates).""" From 22f798d8b61bd9b3f4a79648a466bd30fc1fc98a Mon Sep 17 00:00:00 2001 From: mahid Date: Thu, 9 Mar 2023 12:35:51 -0600 Subject: [PATCH 076/103] Fixed the use of an incorrect operator A mistake was made that made MCprep use the C++ OBJ importer in 3.4 and below, which is incorrect behavior. --- MCprep_addon/world_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index ad7c497c..da1afff0 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -317,7 +317,7 @@ def execute(self, context): res = bpy.ops.wm.obj_import( filepath=self.filepath, use_split_groups=True) else: - res = bpy.ops.wm.obj_import( + res = bpy.ops.import_scene.obj( filepath=self.filepath, use_split_groups=True) except MemoryError as err: print("Memory error during import OBJ:") From 7ac92f64f125857adf3f82aab9fe05c50b1b0da2 Mon Sep 17 00:00:00 2001 From: mahid Date: Thu, 9 Mar 2023 12:55:06 -0600 Subject: [PATCH 077/103] Moved MTL conversion code to seperate function The MTL conversion code was added to a seperate function so that we don't have to deal wtih 2 try-except blocks in the same place. It also gives more flexability in how we approch conversion --- MCprep_addon/world_tools.py | 117 +++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 29ac0bcd..58e203b6 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -87,6 +87,48 @@ def detect_world_exporter(filepath): return 'jmc2obj' +def convert_mtl(filepath): + """Convert the MTL file if we're not using one of Blender's built in colorspaces + + Without this, Blender's OBJ importer will attempt to set non-color data to alpha + maps and what not, which causes issues in ACES and whatnot where non-color data is + not an option + + Returns: + True if success, False if failed + """ + try: + BLENDER_STANDARD = ( + "Standard", + "Filmic", + "Filmic Log", + "Raw", + "False Color" + ) + MTL = filepath.rsplit(".", 1)[0] + '.mtl' + LINES = None + if bpy.context.scene.view_settings.view_transform not in BLENDER_STANDARD: + # This represents a new folder that'll backup the MTL file + original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" # Pathlib is weird when it comes to appending + + # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but we should confirm nonetheless + original_mtl_path.mkdir(parents=True, exist_ok=True) + shutil.copy2(MTL, original_mtl_path.absolute()) # Copy the MTL with metadata + + # Open the MTL + with open(MTL, 'r') as mtl_file: + LINES = mtl_file.readlines() + for index, line in enumerate(LINES): + if line.startswith("map_d"): + LINES[index] = "# " + line + with open(MTL, 'w') as mtl_file: + mtl_file.writelines(LINES) + + except Exception: + return False + return True + + # ----------------------------------------------------------------------------- # open mineways/jmc2obj related # ----------------------------------------------------------------------------- @@ -312,35 +354,12 @@ def execute(self, context): obj_import_mem_msg = ( "Memory error during OBJ import, try exporting a smaller world") try: - try: - BLENDER_STANDARD = ( - "Standard", - "Filmic", - "Filmic Log", - "Raw", - "False Color" - ) - MTL = self.filepath.rsplit(".", 1)[0] + '.mtl' - LINES = None - if bpy.context.scene.view_settings.view_transform not in BLENDER_STANDARD: - # This represents a new folder that'll backup the MTL file - original_mtl_path = Path(self.filepath).parent.absolute() / "ORIGINAL_MTLS" # Pathlib is weird when it comes to appending - - # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but we should confirm nonetheless - original_mtl_path.mkdir(parents=True, exist_ok=True) - shutil.copy2(MTL, original_mtl_path.absolute()) # Copy the MTL with metadata - - # Open the MTL - with open(MTL, 'r') as mtl_file: - LINES = mtl_file.readlines() - for index, line in enumerate(LINES): - if line.startswith("map_d"): - LINES[index] = "# " + line - with open(MTL, 'w') as mtl_file: - mtl_file.writelines(LINES) - - except Exception: - self.report({"ERROR"}, "Failed to convert MTL for compatbility") + # First let's convert the MTL if needed + conv_res = convert_mtl(self.filepath) + if not conv_res: + self.report({"ERROR"}, "MTL conversion failed!") + + # Imported OBJ res = bpy.ops.import_scene.obj( filepath=self.filepath, use_split_groups=True) except MemoryError as err: @@ -894,34 +913,34 @@ def create_dynamic_world(self, context, blendfile, wname): # # update drivers, needed if time has changed vs import source # if context.scene.world.node_tree.animation_data: - # # context.scene.world.node_tree.animation_data.drivers[0].update() - # drivers = context.scene.world.node_tree.animation_data.drivers[0] - # drivers.driver.variables[0].targets[0].id = time_obj - # # nope, still doesn't work. + # # context.scene.world.node_tree.animation_data.drivers[0].update() + # drivers = context.scene.world.node_tree.animation_data.drivers[0] + # drivers.driver.variables[0].targets[0].id = time_obj + # # nope, still doesn't work. # if needed: create time object and setup drivers # if not time_obj: - # conf.log("Creating time_obj") - # time_obj = bpy.data.objects.new('MCprep Time Control', None) - # util.obj_link_scene(time_obj, context) - # global time_obj_cache - # time_obj_cache = time_obj - # if hasattr(time_obj, "empty_draw_type"): # 2.7 - # time_obj.empty_draw_type = 'SPHERE' - # else: # 2.8 - # time_obj.empty_display_type = 'SPHERE' + # conf.log("Creating time_obj") + # time_obj = bpy.data.objects.new('MCprep Time Control', None) + # util.obj_link_scene(time_obj, context) + # global time_obj_cache + # time_obj_cache = time_obj + # if hasattr(time_obj, "empty_draw_type"): # 2.7 + # time_obj.empty_draw_type = 'SPHERE' + # else: # 2.8 + # time_obj.empty_display_type = 'SPHERE' # first, get the driver # if (not world.node_tree.animation_data - # or not world.node_tree.animation_data.drivers - # or not world.node_tree.animation_data.drivers[0].driver): - # conf.log("Could not get driver from imported dynamic world") - # self.report({'WARNING'}, "Could not update driver for dynamic world") - # driver = None + # or not world.node_tree.animation_data.drivers + # or not world.node_tree.animation_data.drivers[0].driver): + # conf.log("Could not get driver from imported dynamic world") + # self.report({'WARNING'}, "Could not update driver for dynamic world") + # driver = None # else: - # driver = world.node_tree.animation_data.drivers[0].driver + # driver = world.node_tree.animation_data.drivers[0].driver # if driver and driver.variables[0].targets[0].id_type == 'OBJECT': - # driver.variables[0].targets[0].id = time_obj + # driver.variables[0].targets[0].id = time_obj # add driver to control obj's x rotation return obj_list From 0cbe395f6c379668be9615d0f8dd1eefe3deb2ab Mon Sep 17 00:00:00 2001 From: mahid Date: Thu, 9 Mar 2023 13:15:54 -0600 Subject: [PATCH 078/103] Added proper caching support for MTL conversion Beyond just not having to perform a useless operation (useful on larger MTLs), it also prevents edited MTLs from being copied into the ORIGINAL_MTLS directory, which is important as it keeps the original MTL intact. This is done by simply adding a header at the end of the MTL file whose existance is then checked. The reason we do it at the end of the file is because: - It's easier - It's less likely to interfere with whatever header a world exporter may create in the MTL file At this point, I think it's safe to say that we're ready for review. --- MCprep_addon/world_tools.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 58e203b6..fa9d79ef 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -107,22 +107,38 @@ def convert_mtl(filepath): ) MTL = filepath.rsplit(".", 1)[0] + '.mtl' LINES = None + with open(MTL, 'r') as mtl_file: + LINES = mtl_file.readlines() + if bpy.context.scene.view_settings.view_transform not in BLENDER_STANDARD: # This represents a new folder that'll backup the MTL file original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" # Pathlib is weird when it comes to appending # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but we should confirm nonetheless original_mtl_path.mkdir(parents=True, exist_ok=True) - shutil.copy2(MTL, original_mtl_path.absolute()) # Copy the MTL with metadata + + MCPREP_HEADER = ( + "# This section was created by MCprep's MTL conversion script\n", + "# Please do not remove\n", + "# Thanks c:\n" + ) + + # Check if MTL has already been converted. If so, return True + if not all(line in LINES for line in MCPREP_HEADER): + shutil.copy2(MTL, original_mtl_path.absolute()) # Copy the MTL with metadata + else: + return True - # Open the MTL + # Otherwise let's continue with open(MTL, 'r') as mtl_file: - LINES = mtl_file.readlines() for index, line in enumerate(LINES): if line.startswith("map_d"): LINES[index] = "# " + line + with open(MTL, 'w') as mtl_file: mtl_file.writelines(LINES) + mtl_file.writelines(MCPREP_HEADER) + except Exception: return False From 29970c16fe6dff80841b4e01a36e2d3003c23fdd Mon Sep 17 00:00:00 2001 From: mahid Date: Thu, 9 Mar 2023 13:28:31 -0600 Subject: [PATCH 079/103] Swapped URL out for OBJ help Technically this URL won't work now but unless GitHub decides to be inconsistent, it will work once `dev` has been merged with `master`. --- MCprep_addon/mcprep_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 20a8e870..9445293c 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -727,7 +727,7 @@ def draw(self, context): row = col.row() row.operator( "mcprep.open_help", text="", icon="QUESTION", emboss=False - ).url = "https://github.com/StandingPadAnimations/MCprep-Kaion/blob/obj-metadata/docs/common_errors.md#obj-not-exported-with-the-correct-settings-for-textureswap" + ).url = "https://github.com/TheDuckCow/MCprep/blob/master/docs/common_errors.md#common-error-messages-and-what-they-mean" row.label(text="OBJ incompatible with textureswap") p = col.operator("mcprep.swap_texture_pack") p.filepath = context.scene.mcprep_texturepack_path From a114d3c208aa13f785263a7cb60a445a8318d0ec Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Fri, 10 Mar 2023 09:20:10 -0500 Subject: [PATCH 080/103] Marking weather particles as fake users. Necessary, since imported particle objects are not loaded into the scene and thus would disappear after a file reload. --- MCprep_addon/spawner/effects.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MCprep_addon/spawner/effects.py b/MCprep_addon/spawner/effects.py index 78479cd3..e7e74e42 100644 --- a/MCprep_addon/spawner/effects.py +++ b/MCprep_addon/spawner/effects.py @@ -137,6 +137,10 @@ def add_area_particle_effect(context, effect, location): post_systems = list(bpy.data.particles) imported_particles = list(set(post_systems) - set(pre_systems))[0] + # Assign particles as fake, to avoid being purged after file reload. + if imported_particles.instance_object: + imported_particles.instance_object.use_fake_user = True + # Assign the active object and selection state. for sel_obj in bpy.context.selected_objects: util.select_set(sel_obj, False) @@ -164,9 +168,6 @@ def add_area_particle_effect(context, effect, location): scene_frames = psystem.settings.frame_end - psystem.settings.frame_start psystem.settings.count = int(density * scene_frames) - # TODO: Attempt to account for a target size. - # TODO: Potentially parent to camera. - return obj From 5497c0c01e2e569fc0f2f9ec924b108d2e15151e Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Fri, 10 Mar 2023 09:53:59 -0500 Subject: [PATCH 081/103] Removed non 2.7x compatible f-string use. Also generally improved whitespace usage for line limit and pep8 in the world_tools file. Only lint issues remaining are the more fundamental issue of how we are using decorators, and some commented out code in the dymaic world execution. --- MCprep_addon/mcprep_ui.py | 17 ++++----- MCprep_addon/world_tools.py | 71 ++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 9445293c..9f084068 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -768,15 +768,16 @@ def draw(self, context): col.operator( "mcprep.improve_ui", text="Improve UI", icon='SETTINGS') - # Optimizer Panel. - row = col.row(align=True) - icon = "TRIA_DOWN" if scn_props.show_settings_optimizer else "TRIA_RIGHT" - row.prop( - scn_props, "show_settings_optimizer", - text="Cycles Optimizer", icon=icon) - if scn_props.show_settings_optimizer: + # Optimizer Panel (only for blender 2.80+) + if util.min_bv((2, 80)): row = col.row(align=True) - optimize_scene.panel_draw(context, row) + icon = "TRIA_DOWN" if scn_props.show_settings_optimizer else "TRIA_RIGHT" + row.prop( + scn_props, "show_settings_optimizer", + text="Cycles Optimizer", icon=icon) + if scn_props.show_settings_optimizer: + row = col.row(align=True) + optimize_scene.panel_draw(context, row) # Advanced settings. row = col.row(align=True) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 49ea1c09..4c2c8e2a 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -36,6 +36,7 @@ time_obj_cache = None + def get_time_object(): """Returns the time object if present in the file""" global time_obj_cache # to avoid re parsing every time @@ -62,21 +63,23 @@ def get_time_object(): return time_obj_cache + class ObjHeaderOptions: + """Wrapper functions to avoid typos causing issues.""" + def __init__(self): self._exporter = None self._file_type = None - - """ - Wrapper functions to avoid typos causing issues - """ + def set_mineways(self): self._exporter = "Mineways" + def set_jmc2obj(self): self._exporter = "jmc2obj" def set_atlas(self): self._file_type = "ATLAS" + def set_seperated(self): self._file_type = "INDIVIDUAL_TILES" @@ -92,7 +95,10 @@ def exporter(self): def texture_type(self): return self._file_type if self._file_type is not None else "NONE" + obj_header = ObjHeaderOptions() + + def detect_world_exporter(filepath): """Detect whether Mineways or jmc2obj was used, based on prefix info. @@ -108,9 +114,12 @@ def detect_world_exporter(filepath): # form of: # Wavefront OBJ file made by Mineways version 5.10... for line in obj_fd: if line.startswith("# File type:"): - header = line.rstrip() # Remove trailing newline - - # The issue here is that Mineways has changed how the header is generated. As such, we're limited with only a couple of OBJs, some from 2020 and some from 2023, so we'll assume people are using an up to date version. + header = line.rstrip() # Remove trailing newline + + # The issue here is that Mineways has changed how the header is generated. + # As such, we're limited with only a couple of OBJs, some from + # 2020 and some from 2023, so we'll assume people are using + # an up to date version. atlas = ( "# File type: Export all textures to three large images", "# File type: Export full color texture patterns" @@ -119,17 +128,19 @@ def detect_world_exporter(filepath): "# File type: Export tiles for textures to directory textures", "# File type: Export individual textures to directory tex" ) - print(f"\"{header}\"") - if header in atlas: # If a texture atlas is used + print('"{}"'.format(header)) + if header in atlas: # If a texture atlas is used obj_header.set_atlas() - elif header in tiles: # If the OBJ uses individual textures + elif header in tiles: # If the OBJ uses individual textures obj_header.set_seperated() return except UnicodeDecodeError: print("failed to read first line of obj: " + filepath) return obj_header.set_jmc2obj() - obj_header.set_seperated() # Since this is the default for Jmc2Obj, we'll assume this is what the OBJ is using + # Since this is the default for Jmc2Obj, + # we'll assume this is what the OBJ is using + obj_header.set_seperated() # ----------------------------------------------------------------------------- # open mineways/jmc2obj related @@ -320,7 +331,8 @@ def execute(self, context): if self.filepath.lower().endswith(".mtl"): filename = Path(self.filepath) new_filename = filename.with_suffix(".obj") - self.filepath = str(new_filename) # Change it from MTL to OBJ, this will be checked with the rest of the if clauses + # Auto change from MTL to OBJ, latet if's will check if existing. + self.filepath = str(new_filename) if not self.filepath: self.report({"ERROR"}, "File not found, could not import obj") return {'CANCELLED'} @@ -359,10 +371,10 @@ def execute(self, context): res = None if util.min_bv((3, 5)): res = bpy.ops.wm.obj_import( - filepath=self.filepath, use_split_groups=True) + filepath=self.filepath, use_split_groups=True) else: res = bpy.ops.import_scene.obj( - filepath=self.filepath, use_split_groups=True) + filepath=self.filepath, use_split_groups=True) except MemoryError as err: print("Memory error during import OBJ:") print(err) @@ -440,7 +452,7 @@ def execute(self, context): prefs = util.get_user_preferences(context) detect_world_exporter(self.filepath) prefs.MCprep_exporter_type = obj_header.exporter() - + for obj in context.selected_objects: obj["MCPREP_OBJ_HEADER"] = True obj["MCPREP_OBJ_FILE_TYPE"] = obj_header.texture_type() @@ -451,7 +463,7 @@ def execute(self, context): addon_prefs = util.get_user_preferences(context) self.track_exporter = addon_prefs.MCprep_exporter_type # Soft detect. return {'FINISHED'} - + def obj_name_to_material(self, obj): """Update an objects name based on its first material""" if not obj: @@ -527,9 +539,12 @@ def prep_world_cycles(self, context): if "mcprep_world" not in context.scene.world: world_nodes.clear() - skynode = generate.create_node(world_nodes, "ShaderNodeTexSky", location = (-280, 300)) - background = generate.create_node(world_nodes, "ShaderNodeBackground", location = (10, 300)) - output = generate.create_node(world_nodes, "ShaderNodeOutputWorld", location = (300, 300)) + skynode = generate.create_node( + world_nodes, "ShaderNodeTexSky", location=(-280, 300)) + background = generate.create_node( + world_nodes, "ShaderNodeBackground", location=(10, 300)) + output = generate.create_node( + world_nodes, "ShaderNodeOutputWorld", location=(300, 300)) world_links.new(skynode.outputs["Color"], background.inputs[0]) world_links.new(background.outputs["Background"], output.inputs[0]) @@ -559,11 +574,16 @@ def prep_world_eevee(self, context): if "mcprep_world" not in context.scene.world: world_nodes.clear() - light_paths = generate.create_node(world_nodes, "ShaderNodeLightPath", location = (-150, 400)) - background_camera = generate.create_node(world_nodes, "ShaderNodeBackground", location = (10, 150)) - background_others = generate.create_node(world_nodes, "ShaderNodeBackground", location = (10, 300)) - mix_shader =generate.create_node(world_nodes, "ShaderNodeMixShader", location = (300, 300)) - output = generate.create_node(world_nodes, "ShaderNodeOutputWorld", location = (500, 300)) + light_paths = generate.create_node( + world_nodes, "ShaderNodeLightPath", location=(-150, 400)) + background_camera = generate.create_node( + world_nodes, "ShaderNodeBackground", location=(10, 150)) + background_others = generate.create_node( + world_nodes, "ShaderNodeBackground", location=(10, 300)) + mix_shader = generate.create_node( + world_nodes, "ShaderNodeMixShader", location=(300, 300)) + output = generate.create_node( + world_nodes, "ShaderNodeOutputWorld", location=(500, 300)) background_others.inputs["Color"].default_value = (0.14965, 0.425823, 1, 1) background_others.inputs["Strength"].default_value = 0.1 background_camera.inputs["Color"].default_value = (0.14965, 0.425823, 1, 1) @@ -890,8 +910,9 @@ def create_dynamic_world(self, context, blendfile, wname): if time_obj_cache: try: util.obj_unlink_remove(time_obj_cache, True, context) - except: + except Exception as e: print("Error, could not unlink time_obj_cache " + str(time_obj_cache)) + print(e) time_obj_cache = None # force reset to use newer cache object From 009beb3c9b510559f6decd60dbb6f81d433c171b Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Fri, 10 Mar 2023 09:54:29 -0500 Subject: [PATCH 082/103] Removed unused world time update function. --- MCprep_addon/world_tools.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 4c2c8e2a..31e965e7 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -1026,24 +1026,6 @@ def execute(self, context): return {'FINISHED'} -def world_time_update(self, context): - """Handler which updates the current world time on a frame change. - - Maybe don't need this in favor of using a driver for simplicity - """ - - time = context.scene.mcprep_props.world_time - - # translate time into rotation of sun/moon rig - # see: http://minecraft.gamepedia.com/Day-night_cycle - # set to the armature.... would be even better if it was somehow driver-set. - - # if real python code requried to set this up, generate and auto-run python - # script, though more ideally just set drivers based on the time param - - return - - class MCPREP_OT_render_helper(): render_queue = [] render_queue_cleanup = [] From 068ac000655c0a8634707c46e94eb34078cd9bd0 Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 10:22:43 -0600 Subject: [PATCH 083/103] Fixed formatting Formatting is now based on PEP8 guidlines --- MCprep_addon/world_tools.py | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index fa9d79ef..75a0b5fe 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -88,17 +88,18 @@ def detect_world_exporter(filepath): def convert_mtl(filepath): - """Convert the MTL file if we're not using one of Blender's built in colorspaces + """Convert the MTL file if we're not using one of Blender's built in + colorspaces + + Without this, Blender's OBJ importer will attempt to set non-color data to + alpha maps and what not, which causes issues in ACES and whatnot where + non-color data is not an option. - Without this, Blender's OBJ importer will attempt to set non-color data to alpha - maps and what not, which causes issues in ACES and whatnot where non-color data is - not an option - Returns: True if success, False if failed """ try: - BLENDER_STANDARD = ( + blender_standard = ( "Standard", "Filmic", "Filmic Log", @@ -109,23 +110,26 @@ def convert_mtl(filepath): LINES = None with open(MTL, 'r') as mtl_file: LINES = mtl_file.readlines() - - if bpy.context.scene.view_settings.view_transform not in BLENDER_STANDARD: - # This represents a new folder that'll backup the MTL file - original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" # Pathlib is weird when it comes to appending - - # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but we should confirm nonetheless + + if bpy.context.scene.view_settings.view_transform not in blender_standard: + # This represents a new folder that'll backup the MTL filepath + + # Pathlib is weird when it comes to appending + original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" + # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but + # we should confirm nonetheless original_mtl_path.mkdir(parents=True, exist_ok=True) - + MCPREP_HEADER = ( "# This section was created by MCprep's MTL conversion script\n", "# Please do not remove\n", "# Thanks c:\n" ) - + # Check if MTL has already been converted. If so, return True if not all(line in LINES for line in MCPREP_HEADER): - shutil.copy2(MTL, original_mtl_path.absolute()) # Copy the MTL with metadata + # Copy the MTL with metadata + shutil.copy2(MTL, original_mtl_path.absolute()) else: return True @@ -134,14 +138,14 @@ def convert_mtl(filepath): for index, line in enumerate(LINES): if line.startswith("map_d"): LINES[index] = "# " + line - + with open(MTL, 'w') as mtl_file: mtl_file.writelines(LINES) mtl_file.writelines(MCPREP_HEADER) - except Exception: return False + return True From 320fb54e9708c83ec01b0ed50b5b7548ae7d1fee Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 10:27:29 -0600 Subject: [PATCH 084/103] Minimized try-except block Exceptions are also printed now --- MCprep_addon/world_tools.py | 70 ++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 75a0b5fe..bd7ee69a 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -98,41 +98,39 @@ def convert_mtl(filepath): Returns: True if success, False if failed """ - try: - blender_standard = ( - "Standard", - "Filmic", - "Filmic Log", - "Raw", - "False Color" + blender_standard = ( + "Standard", + "Filmic", + "Filmic Log", + "Raw", + "False Color" + ) + MTL = filepath.rsplit(".", 1)[0] + '.mtl' + LINES = None + with open(MTL, 'r') as mtl_file: + LINES = mtl_file.readlines() + + if bpy.context.scene.view_settings.view_transform not in blender_standard: + # This represents a new folder that'll backup the MTL filepath + original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" + # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but + # we should confirm nonetheless + original_mtl_path.mkdir(parents=True, exist_ok=True) + + MCPREP_HEADER = ( + "# This section was created by MCprep's MTL conversion script\n", + "# Please do not remove\n", + "# Thanks c:\n" ) - MTL = filepath.rsplit(".", 1)[0] + '.mtl' - LINES = None - with open(MTL, 'r') as mtl_file: - LINES = mtl_file.readlines() - - if bpy.context.scene.view_settings.view_transform not in blender_standard: - # This represents a new folder that'll backup the MTL filepath - - # Pathlib is weird when it comes to appending - original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" - # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but - # we should confirm nonetheless - original_mtl_path.mkdir(parents=True, exist_ok=True) - - MCPREP_HEADER = ( - "# This section was created by MCprep's MTL conversion script\n", - "# Please do not remove\n", - "# Thanks c:\n" - ) - - # Check if MTL has already been converted. If so, return True - if not all(line in LINES for line in MCPREP_HEADER): - # Copy the MTL with metadata - shutil.copy2(MTL, original_mtl_path.absolute()) - else: - return True + # Check if MTL has already been converted. If so, return True + if not all(line in LINES for line in MCPREP_HEADER): + # Copy the MTL with metadata + shutil.copy2(MTL, original_mtl_path.absolute()) + else: + return True + + try: # Otherwise let's continue with open(MTL, 'r') as mtl_file: for index, line in enumerate(LINES): @@ -143,9 +141,9 @@ def convert_mtl(filepath): mtl_file.writelines(LINES) mtl_file.writelines(MCPREP_HEADER) - except Exception: - return False - + except Exception as e: + print(e) + return False return True From 9a067dde594e2f8f1d966f8620bd950b8e96008b Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 10:30:05 -0600 Subject: [PATCH 085/103] Added try-except around copy operation The copy operation now performs in a try-except loop for exception handling --- MCprep_addon/world_tools.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index bd7ee69a..a4e80d6e 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -123,12 +123,16 @@ def convert_mtl(filepath): "# Thanks c:\n" ) - # Check if MTL has already been converted. If so, return True - if not all(line in LINES for line in MCPREP_HEADER): - # Copy the MTL with metadata - shutil.copy2(MTL, original_mtl_path.absolute()) - else: - return True + try: + # Check if MTL has already been converted. If so, return True + if not all(line in LINES for line in MCPREP_HEADER): + # Copy the MTL with metadata + shutil.copy2(MTL, original_mtl_path.absolute()) + else: + return True + except Exception as e: + print(e) + return False try: # Otherwise let's continue From d98914d56bf2d26e2ef210cb225215990fe3af6d Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 10:45:38 -0600 Subject: [PATCH 086/103] Some changes Moved call to convert_mtl outside of try-except (because it already has exception handling internally) and changed the reporting to a warning (since it shouldn't affect execution) --- MCprep_addon/world_tools.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index a4e80d6e..46ac7960 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -375,11 +375,12 @@ def execute(self, context): "import again.") obj_import_mem_msg = ( "Memory error during OBJ import, try exporting a smaller world") + + # First let's convert the MTL if needed + conv_res = convert_mtl(self.filepath) try: - # First let's convert the MTL if needed - conv_res = convert_mtl(self.filepath) if not conv_res: - self.report({"ERROR"}, "MTL conversion failed!") + self.report({"WARNING"}, "MTL conversion failed!") # Imported OBJ res = bpy.ops.import_scene.obj( From f71d874d8769a130fd4af941ef17f1823d35f3ae Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 10:54:17 -0600 Subject: [PATCH 087/103] Fixed variable formatting All variables should now follow PEP8 --- MCprep_addon/world_tools.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 46ac7960..e91a6ce9 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -105,10 +105,10 @@ def convert_mtl(filepath): "Raw", "False Color" ) - MTL = filepath.rsplit(".", 1)[0] + '.mtl' - LINES = None - with open(MTL, 'r') as mtl_file: - LINES = mtl_file.readlines() + mtl = filepath.rsplit(".", 1)[0] + '.mtl' + lines = None + with open(mtl, 'r') as mtl_file: + lines = mtl_file.readlines() if bpy.context.scene.view_settings.view_transform not in blender_standard: # This represents a new folder that'll backup the MTL filepath @@ -117,7 +117,7 @@ def convert_mtl(filepath): # we should confirm nonetheless original_mtl_path.mkdir(parents=True, exist_ok=True) - MCPREP_HEADER = ( + mcprep_header = ( "# This section was created by MCprep's MTL conversion script\n", "# Please do not remove\n", "# Thanks c:\n" @@ -125,9 +125,9 @@ def convert_mtl(filepath): try: # Check if MTL has already been converted. If so, return True - if not all(line in LINES for line in MCPREP_HEADER): + if not all(line in lines for line in mcprep_header): # Copy the MTL with metadata - shutil.copy2(MTL, original_mtl_path.absolute()) + shutil.copy2(mtl, original_mtl_path.absolute()) else: return True except Exception as e: @@ -136,14 +136,14 @@ def convert_mtl(filepath): try: # Otherwise let's continue - with open(MTL, 'r') as mtl_file: - for index, line in enumerate(LINES): + with open(mtl, 'r') as mtl_file: + for index, line in enumerate(lines): if line.startswith("map_d"): - LINES[index] = "# " + line + lines[index] = "# " + line - with open(MTL, 'w') as mtl_file: - mtl_file.writelines(LINES) - mtl_file.writelines(MCPREP_HEADER) + with open(mtl, 'w') as mtl_file: + mtl_file.writelines(lines) + mtl_file.writelines(mcprep_header) except Exception as e: print(e) From f05ff075a745e4ab3c7f019c19a03eaf1924baeb Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 11:20:59 -0600 Subject: [PATCH 088/103] Changed how header is checked Since MCprep will always output its header at the end of the MTL, we can simply check the last 3 lines of the MTL, which removes an unnecesary for loop, and makes the code easier to read. --- MCprep_addon/world_tools.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index e91a6ce9..30456dfe 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -124,9 +124,11 @@ def convert_mtl(filepath): ) try: + header = tuple(lines[-3:]) # Get the last 3 lines # Check if MTL has already been converted. If so, return True - if not all(line in lines for line in mcprep_header): + if header != mcprep_header: # Copy the MTL with metadata + print("Header " + str(header)) shutil.copy2(mtl, original_mtl_path.absolute()) else: return True From 9285f98d9f182965f485af52916d1b148a46ae24 Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 11:38:22 -0600 Subject: [PATCH 089/103] Added a comment explaning the conversion --- MCprep_addon/world_tools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 30456dfe..71493503 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -136,8 +136,11 @@ def convert_mtl(filepath): print(e) return False + # In this section, we go over each line + # and check to see if it begins with map_d. If + # it does, then we simply comment it out. Otherwise, + # we can safely ignore it. try: - # Otherwise let's continue with open(mtl, 'r') as mtl_file: for index, line in enumerate(lines): if line.startswith("map_d"): From 961126a246c5801863cb00adf09795e05c4cef42 Mon Sep 17 00:00:00 2001 From: mahid Date: Fri, 10 Mar 2023 11:51:45 -0600 Subject: [PATCH 090/103] Added info on Pylint and Flake8 Both of these programs are useful when conforming to PEP8 guidelines, hence the recommendation to use them. --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b29bdf5..5e1e025d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -237,4 +237,12 @@ Next we need to install `fake-bpy`: If you use PyCharm, you should check the GitHub for [additional instructions](https://github.com/nutti/fake-bpy-module#install-via-pip-package) +### Pylint +MCprep mostly tries to follow the PEP8 guidelines, so it's also a good idea to install pylsp and flake8 for IDEs. + +First, install the 2: +`python3 -m pip install python-lsp-server flake8` + +Then set up your IDE to use pylsp as your Python LSP. This depends on the IDE, so look at the documentation to see how to set your Python LSP for your specific editor. + Now you're ready to do MCprep development From 10337d6a1ba82b860a4938884352b59442f8ea23 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sat, 11 Mar 2023 15:17:09 -0500 Subject: [PATCH 091/103] Edit sync file button and using json lookup for sync mats. This greatly improves the usability of the sync materials function, as users can now easily open to edit the sync file (matching their resource pack), as well as use the MCprep built in generate materials function and still have the material lookups match from their imported OBJs. Before this change, materials had to be exact matches, and there was no easy way to get to the sync material file or to create your own sync file for a custom resource pack without very manual steps. --- MCprep_addon/materials/sync.py | 89 ++++++++++++++++++++++++++-------- MCprep_addon/mcprep_ui.py | 5 +- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/MCprep_addon/materials/sync.py b/MCprep_addon/materials/sync.py index 99cd772c..86169b22 100644 --- a/MCprep_addon/materials/sync.py +++ b/MCprep_addon/materials/sync.py @@ -22,6 +22,7 @@ import bpy from bpy.app.handlers import persistent +from . import generate from .. import conf from .. import tracking from .. import util @@ -55,28 +56,35 @@ def reload_material_sync_library(context): conf.log("Updated sync cache", vv_only=True) -def material_in_sync_library(material, context): +def material_in_sync_library(mat_name, context): """Returns true if the material is in the sync mat library blend file""" if conf.material_sync_cache is None: reload_material_sync_library(context) - if util.nameGeneralize(material.name) in conf.material_sync_cache: + if util.nameGeneralize(mat_name) in conf.material_sync_cache: return True - elif material.name in conf.material_sync_cache: + elif mat_name in conf.material_sync_cache: return True return False -def sync_material(context, material, link, replace): +def sync_material(context, source_mat, sync_mat_name, link, replace): """If found, load and apply the material found in a library. + Args: + context: Current bpy context. + source_mat: bpy.Types.Material in the current open blend file + sync_mat_name: str of the mat to try to replace in. + link: Bool, if true then new material is linkd instead of appended. + replace: If true, the old material in this file is immediately rm'd. + Returns: 0 if nothing modified, 1 if modified None if no error or string if error """ - if material.name in conf.material_sync_cache: - import_name = material.name - elif util.nameGeneralize(material.name) in conf.material_sync_cache: - import_name = util.nameGeneralize(material.name) + if sync_mat_name in conf.material_sync_cache: + import_name = sync_mat_name + elif util.nameGeneralize(sync_mat_name) in conf.material_sync_cache: + import_name = util.nameGeneralize(sync_mat_name) # if link is true, check library material not already linked sync_file = get_sync_blend(context) @@ -85,16 +93,16 @@ def sync_material(context, material, link, replace): imported = set(bpy.data.materials[:]) - set(init_mats) if not imported: - return 0, "Could not import " + str(material.name) + return 0, "Could not import {}".format(import_name) new_material = list(imported)[0] # 2.78+ only, else silent failure - res = util.remap_users(material, new_material) + res = util.remap_users(source_mat, new_material) if res != 0: # try a fallback where we at least go over the selected objects return 0, res if replace is True: - bpy.data.materials.remove(material) + bpy.data.materials.remove(source_mat) return 1, None @@ -156,22 +164,33 @@ def execute(self, context): # gets the list of materials (without repetition) from selected mat_list = util.materialsFromObj(obj_list) - if not mat_list: - if not self.skipUsage: - self.report({'ERROR'}, "No materials found on selected objects") - return {'CANCELLED'} else: mat_list = list(bpy.data.materials) + if not mat_list: + if not self.skipUsage: + self.report({'ERROR'}, "No materials to sync") + return {'CANCELLED'} + # consider force reloading the material library blend eligible = 0 modified = 0 last_err = None for mat in mat_list: - if not material_in_sync_library(mat, context): + # Match either exact name, or translated name. + lookup_match, _ = generate.get_mc_canonical_name(mat.name) + if material_in_sync_library(mat.name, context) is True: + sync_mat_name = mat.name + elif material_in_sync_library(lookup_match, context) is True: + sync_mat_name = lookup_match + else: continue affected, err = sync_material( - context, mat, self.link, self.replace_materials) + context, + source_mat=mat, + sync_mat_name=sync_mat_name, + link=self.link, + replace=self.replace_materials) eligible += 1 modified += affected if err: @@ -202,6 +221,37 @@ def execute(self, context): return {'FINISHED'} +class MCPREP_OT_edit_sync_materials_file(bpy.types.Operator): + """Open the the file used fo syncrhonization.""" + bl_idname = "mcprep.edit_sync_materials_file" + bl_label = "Edit sync file" + bl_options = {'REGISTER', 'UNDO'} + + track_function = "edit_sync_materials" + track_param = None + @tracking.report_error + def execute(self, context): + file = get_sync_blend(context) + if not bpy.data.is_saved: + self.report({'ERROR'}, "Save your blend file first") + return {'CANCELLED'} + + # Will open without saving or prompting! + # TODO: Perform action more similar to the asset browser, which opens + # a new instance of blender. + if os.path.isfile(file): + bpy.ops.wm.open_mainfile(filepath=file) + else: + # Open and save a new sync file instead. + bpy.ops.wm.read_homefile(use_empty=True) + + # Set the local resource pack to match this generated file. + bpy.context.scene.mcprep_texturepack_path = "//" + + bpy.ops.wm.save_as_mainfile(filepath=file) + return {'FINISHED'} + + # ----------------------------------------------------------------------------- # Registration # ----------------------------------------------------------------------------- @@ -209,6 +259,7 @@ def execute(self, context): classes = ( MCPREP_OT_sync_materials, + MCPREP_OT_edit_sync_materials_file, ) @@ -224,5 +275,5 @@ def unregister(): bpy.utils.unregister_class(cls) try: bpy.app.handlers.load_post.remove(clear_sync_cache) - except: - pass + except Exception as e: + print("Unregister post handler error: ", e) diff --git a/MCprep_addon/mcprep_ui.py b/MCprep_addon/mcprep_ui.py index 9f084068..f69493ac 100644 --- a/MCprep_addon/mcprep_ui.py +++ b/MCprep_addon/mcprep_ui.py @@ -805,7 +805,10 @@ def draw(self, context): b_row = box.row() b_col = b_row.column(align=True) - b_col.operator("mcprep.sync_materials") + sync_row = b_col.row(align=True) + sync_row.operator("mcprep.sync_materials") + sync_row.operator( + "mcprep.edit_sync_materials_file", text="", icon="FILE_TICK") # or TOOL_SETTINGS. b_col.operator("mcprep.replace_missing_textures") b_col.operator("mcprep.animate_textures") # TODO: operator to make all local, all packed, or set to other location From 56d3d4a6616885ecba01b2ef7fb111119d727fb5 Mon Sep 17 00:00:00 2001 From: mahid Date: Sat, 11 Mar 2023 17:11:30 -0600 Subject: [PATCH 092/103] Added MTL recovery if exception occurs If an exception occurs when writing to the file, we want to gracefully recover, hence the copying the original MTL back. TODO: Confirm if `mtl` will be an absolute path --- MCprep_addon/world_tools.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 71493503..710796cd 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -107,6 +107,7 @@ def convert_mtl(filepath): ) mtl = filepath.rsplit(".", 1)[0] + '.mtl' lines = None + copied_file = None with open(mtl, 'r') as mtl_file: lines = mtl_file.readlines() @@ -129,7 +130,7 @@ def convert_mtl(filepath): if header != mcprep_header: # Copy the MTL with metadata print("Header " + str(header)) - shutil.copy2(mtl, original_mtl_path.absolute()) + copied_file = shutil.copy2(mtl, original_mtl_path.absolute()) else: return True except Exception as e: @@ -145,13 +146,20 @@ def convert_mtl(filepath): for index, line in enumerate(lines): if line.startswith("map_d"): lines[index] = "# " + line - + except Exception as e: + print(e) + return False + + # This needs to be seperate since it involves writing + try: with open(mtl, 'w') as mtl_file: mtl_file.writelines(lines) mtl_file.writelines(mcprep_header) + # Recover the original file except Exception as e: print(e) + shutil.copy2(copied_file, mtl) return False return True From 9e59bdcdf1092320cf5cfa50a3fa46ebe61d2334 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 11:00:03 -0700 Subject: [PATCH 093/103] Adding unit test for convert_mtl with test files. Ensures that the behavior is effective and avoids edges cases. --- run_tests.sh | 2 ++ test_files/addon_tests.py | 53 +++++++++++++++++++++++++++++- test_files/mtl_atlas_modified.mtl | 3 ++ test_files/mtl_atlas_original.mtl | 3 ++ test_files/mtl_simple_modified.mtl | 3 ++ test_files/mtl_simple_original.mtl | 3 ++ 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 test_files/mtl_atlas_modified.mtl create mode 100644 test_files/mtl_atlas_original.mtl create mode 100644 test_files/mtl_simple_modified.mtl create mode 100644 test_files/mtl_simple_original.mtl diff --git a/run_tests.sh b/run_tests.sh index 6c42f518..28d6f196 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -13,6 +13,8 @@ # # Run only a single unit test, but across all blender versions # ./run_tests.sh -all -run change_skin +# +# Add -v to any argument above to allow print statements within tests. # File containing 1 line per blender executable complete path. The first # line is the blender executable that will be used in 'quick' (-single) tests. diff --git a/test_files/addon_tests.py b/test_files/addon_tests.py index ba04a723..c296a8da 100644 --- a/test_files/addon_tests.py +++ b/test_files/addon_tests.py @@ -23,6 +23,7 @@ from contextlib import redirect_stdout +import filecmp import importlib import io import os @@ -94,6 +95,7 @@ def __init__(self): self.test_generate_material_sequence, self.qa_effects, self.qa_rigs, + self.convert_mtl_simple, ] self.run_only = None # Name to give to only run this test @@ -2145,6 +2147,55 @@ def qa_rigs(self): if issues: return issues + def convert_mtl_simple(self): + """Ensures that conversion of the mtl with other color space works.""" + from MCprep import world_tools + + src = "mtl_simple_original.mtl" + end = "mtl_simple_modified.mtl" + test_dir = os.path.dirname(__file__) + simple_mtl = os.path.join(test_dir, src) + modified_mtl = os.path.join(test_dir, end) + + # now save the texturefile somewhere + tmp_dir = tempfile.gettempdir() + tmp_mtl = os.path.join(tmp_dir, src) + shutil.copyfile(simple_mtl, tmp_mtl) # leave original intact + + if not os.path.isfile(tmp_mtl): + return "Failed to create tmp tml at " + tmp_mtl + + # Need to mock: + # bpy.context.scene.view_settings.view_transform + # to be an invalid kind of attribute, to simulate an ACES or AgX space. + # But we can't do that since we're not (yet) using the real unittest + # framework, hence we'll just clear the world_tool's vars. + save_init = list(world_tools.BUILTIN_SPACES) + world_tools.BUILTIN_SPACES = ["NotRealSpace"] + print("TEST: pre", world_tools.BUILTIN_SPACES) + + # Resultant file + res = world_tools.convert_mtl(tmp_mtl) + + # Restore the property we unset. + world_tools.BUILTIN_SPACES = save_init + print("TEST: post", world_tools.BUILTIN_SPACES) + + if res is None: + return "Failed to mock color space and thus could not test convert_mtl" + + if res is False: + return "Convert mtl failed with false response" + + # Now check that the data is the same. + res = filecmp.cmp(tmp_mtl, modified_mtl, shallow=False) + if res is not True: + # Not removing file, since we likely want to inspect it. + return "Generated MTL is different: {} vs {}".format( + tmp_mtl, modified_mtl) + else: + os.remove(tmp_mtl) + class OCOL: """override class for colors, for terminals not supporting color-out""" @@ -2206,7 +2257,7 @@ class MCPTEST_OT_test_selfdestruct(bpy.types.Operator): def execute(self, context): print("De-registering MCprep test") unregister() - return{'FINISHED'} + return {'FINISHED'} class MCPTEST_PT_test_panel(bpy.types.Panel): diff --git a/test_files/mtl_atlas_modified.mtl b/test_files/mtl_atlas_modified.mtl new file mode 100644 index 00000000..93c5ee55 --- /dev/null +++ b/test_files/mtl_atlas_modified.mtl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19f5ecb393667f9079d7803ef0692136ee3a9405c07ed8bdde59039b2c82f50f +size 6239 diff --git a/test_files/mtl_atlas_original.mtl b/test_files/mtl_atlas_original.mtl new file mode 100644 index 00000000..d3ba3786 --- /dev/null +++ b/test_files/mtl_atlas_original.mtl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6512171c4bfe37225dfefb9e1362f3694bb3a6f6d9e5e2ffd94c970ff42c616f +size 6123 diff --git a/test_files/mtl_simple_modified.mtl b/test_files/mtl_simple_modified.mtl new file mode 100644 index 00000000..9a848a1f --- /dev/null +++ b/test_files/mtl_simple_modified.mtl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:271bb14a3ea6827d45f86158af6a4f3b857f6714115fff9839a119031e3c8ad8 +size 256 diff --git a/test_files/mtl_simple_original.mtl b/test_files/mtl_simple_original.mtl new file mode 100644 index 00000000..9550243a --- /dev/null +++ b/test_files/mtl_simple_original.mtl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50ea05abc54c703bf70023a7f55bbce216d2a98c5b8f4589ad571c4da5e52325 +size 156 From f8ff838b34ed26176abf4ab856cca5a064e80da5 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 11:03:41 -0700 Subject: [PATCH 094/103] Transforming the convert_mtl function to be testable. Had to do this transform since we don't want to install / uninstall color spaces for tests. Also, pivoted to return early and return None if nothing processed, so we can test for that too. --- MCprep_addon/world_tools.py | 126 ++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 451cfabc..b64ff11f 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -34,6 +34,14 @@ # supporting functions # ----------------------------------------------------------------------------- +BUILTIN_SPACES = ( + "Standard", + "Filmic", + "Filmic Log", + "Raw", + "False Color" +) + time_obj_cache = None @@ -143,6 +151,7 @@ def detect_world_exporter(filepath): # we'll assume this is what the OBJ is using obj_header.set_seperated() + def convert_mtl(filepath): """Convert the MTL file if we're not using one of Blender's built in colorspaces @@ -151,72 +160,73 @@ def convert_mtl(filepath): alpha maps and what not, which causes issues in ACES and whatnot where non-color data is not an option. + This MTL conversion simply does the following: + - Comment out lines that begin with map_d + - Add a header at the end + Returns: - True if success, False if failed + True if success or skipped, False if failed, or None if skipped """ - blender_standard = ( - "Standard", - "Filmic", - "Filmic Log", - "Raw", - "False Color" - ) + print("Convert MTL's spaces??", BUILTIN_SPACES) mtl = filepath.rsplit(".", 1)[0] + '.mtl' lines = None copied_file = None with open(mtl, 'r') as mtl_file: lines = mtl_file.readlines() - if bpy.context.scene.view_settings.view_transform not in blender_standard: - # This represents a new folder that'll backup the MTL filepath - original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" - # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but - # we should confirm nonetheless - original_mtl_path.mkdir(parents=True, exist_ok=True) + if bpy.context.scene.view_settings.view_transform in BUILTIN_SPACES: + return None - mcprep_header = ( - "# This section was created by MCprep's MTL conversion script\n", - "# Please do not remove\n", - "# Thanks c:\n" - ) + # This represents a new folder that'll backup the MTL filepath + original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" + # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but + # we should confirm nonetheless + original_mtl_path.mkdir(parents=True, exist_ok=True) + + mcprep_header = ( + "# This section was created by MCprep's MTL conversion script\n", + "# Please do not remove\n", + "# Thanks c:\n" + ) + + try: + header = tuple(lines[-3:]) # Get the last 3 lines + # Check if MTL has already been converted. If so, return True + if header != mcprep_header: + # Copy the MTL with metadata + print("Header " + str(header)) + copied_file = shutil.copy2(mtl, original_mtl_path.absolute()) + else: + return True + except Exception as e: + print(e) + return False + + # In this section, we go over each line + # and check to see if it begins with map_d. If + # it does, then we simply comment it out. Otherwise, + # we can safely ignore it. + try: + with open(mtl, 'r') as mtl_file: + for index, line in enumerate(lines): + if line.startswith("map_d"): + lines[index] = "# " + line + except Exception as e: + print(e) + return False + + # This needs to be seperate since it involves writing + try: + with open(mtl, 'w') as mtl_file: + mtl_file.writelines(lines) + mtl_file.writelines(mcprep_header) + + # Recover the original file + except Exception as e: + print(e) + shutil.copy2(copied_file, mtl) + return False - try: - header = tuple(lines[-3:]) # Get the last 3 lines - # Check if MTL has already been converted. If so, return True - if header != mcprep_header: - # Copy the MTL with metadata - print("Header " + str(header)) - copied_file = shutil.copy2(mtl, original_mtl_path.absolute()) - else: - return True - except Exception as e: - print(e) - return False - - # In this section, we go over each line - # and check to see if it begins with map_d. If - # it does, then we simply comment it out. Otherwise, - # we can safely ignore it. - try: - with open(mtl, 'r') as mtl_file: - for index, line in enumerate(lines): - if line.startswith("map_d"): - lines[index] = "# " + line - except Exception as e: - print(e) - return False - - # This needs to be seperate since it involves writing - try: - with open(mtl, 'w') as mtl_file: - mtl_file.writelines(lines) - mtl_file.writelines(mcprep_header) - - # Recover the original file - except Exception as e: - print(e) - shutil.copy2(copied_file, mtl) - return False return True @@ -449,7 +459,9 @@ def execute(self, context): # First let's convert the MTL if needed conv_res = convert_mtl(self.filepath) try: - if not conv_res: + if conv_res is None: + pass # skipped, no issue anyways. + elif conv_res is False: self.report({"WARNING"}, "MTL conversion failed!") res = None From d6903473fead2f0be00314deaa75a909cc62bf47 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 12:56:27 -0700 Subject: [PATCH 095/103] Added space to convert_mtl check to avoid false positives --- MCprep_addon/world_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index b64ff11f..02adca90 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -209,7 +209,7 @@ def convert_mtl(filepath): try: with open(mtl, 'r') as mtl_file: for index, line in enumerate(lines): - if line.startswith("map_d"): + if line.startswith("map_d "): lines[index] = "# " + line except Exception as e: print(e) From 59249fbf2fb782342e0b5992a6d0dcf8b0db9193 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 13:39:18 -0700 Subject: [PATCH 096/103] Added skip test to unit tests for convert_mtl. --- test_files/addon_tests.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test_files/addon_tests.py b/test_files/addon_tests.py index c296a8da..4546e3f0 100644 --- a/test_files/addon_tests.py +++ b/test_files/addon_tests.py @@ -96,6 +96,7 @@ def __init__(self): self.qa_effects, self.qa_rigs, self.convert_mtl_simple, + self.convert_mtl_skip, ] self.run_only = None # Name to give to only run this test @@ -2196,6 +2197,43 @@ def convert_mtl_simple(self): else: os.remove(tmp_mtl) + def convert_mtl_skip(self): + """Ensures that we properly skip if a built in space active.""" + from MCprep import world_tools + + src = "mtl_simple_original.mtl" + test_dir = os.path.dirname(__file__) + simple_mtl = os.path.join(test_dir, src) + + # now save the texturefile somewhere + tmp_dir = tempfile.gettempdir() + tmp_mtl = os.path.join(tmp_dir, src) + shutil.copyfile(simple_mtl, tmp_mtl) # leave original intact + + if not os.path.isfile(tmp_mtl): + return "Failed to create tmp tml at " + tmp_mtl + + # Need to mock: + # bpy.context.scene.view_settings.view_transform + # to be an invalid kind of attribute, to simulate an ACES or AgX space. + # But we can't do that since we're not (yet) using the real unittest + # framework, hence we'll just clear the world_tool's vars. + actual_space = str(bpy.context.scene.view_settings.view_transform) + save_init = list(world_tools.BUILTIN_SPACES) + world_tools.BUILTIN_SPACES = [actual_space] + print("TEST: pre", world_tools.BUILTIN_SPACES) + + # Resultant file + res = world_tools.convert_mtl(tmp_mtl) + + # Restore the property we unset. + world_tools.BUILTIN_SPACES = save_init + print("TEST: post", world_tools.BUILTIN_SPACES) + + if res is not None: + os.remove(tmp_mtl) + return "Should not have converter MTL for valid space" + class OCOL: """override class for colors, for terminals not supporting color-out""" From 3f792afea9b5b3c01418b99e9719e0cf12e1445b Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 14:23:55 -0700 Subject: [PATCH 097/103] Removed debug print --- MCprep_addon/world_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 02adca90..15f336d9 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -167,7 +167,6 @@ def convert_mtl(filepath): Returns: True if success or skipped, False if failed, or None if skipped """ - print("Convert MTL's spaces??", BUILTIN_SPACES) mtl = filepath.rsplit(".", 1)[0] + '.mtl' lines = None copied_file = None From 52397371eb513de7991f63321aaed29e8f9fa9f7 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 15:03:32 -0700 Subject: [PATCH 098/103] removed confirmed resolved todo --- MCprep_addon/world_tools.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/MCprep_addon/world_tools.py b/MCprep_addon/world_tools.py index 15f336d9..8b66e4d5 100644 --- a/MCprep_addon/world_tools.py +++ b/MCprep_addon/world_tools.py @@ -178,8 +178,6 @@ def convert_mtl(filepath): # This represents a new folder that'll backup the MTL filepath original_mtl_path = Path(filepath).parent.absolute() / "ORIGINAL_MTLS" - # TODO: make sure this works in 2.7x. It should since 2.8 uses 3.7 but - # we should confirm nonetheless original_mtl_path.mkdir(parents=True, exist_ok=True) mcprep_header = ( From e0a503e866fa56cd21033382328cea51585059c4 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 15:27:26 -0700 Subject: [PATCH 099/103] Addressing longstanding unnecessary mineways unittest failure Never going to have full coverage of this atlas format anyways, so we just skip relevant errors for lack of material coverage --- test_files/addon_tests.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test_files/addon_tests.py b/test_files/addon_tests.py index 4546e3f0..d2de7191 100644 --- a/test_files/addon_tests.py +++ b/test_files/addon_tests.py @@ -892,6 +892,11 @@ def import_materials_util(self, mapping_set): if len(mapped) == 0: return "No materials mapped" + elif mapping_set == "block_mapping_mineways" and len(mapped) > 75: + # Known that the "combined" atlas texture of Mineways imports + # has low coverge, and we're not doing anything about it. + # but will at least capture if coverage gets *worse*. + pass elif len(mapped) < len(unmapped): # too many esp. for Mineways # not a very optimistic threshold, but better than none return "More materials unmapped than mapped" @@ -902,7 +907,7 @@ def import_materials_util(self, mapping_set): # no matching canon name (warn) mats_not_canon = [itm[0] for itm in mapped if itm[1] is None] - if mats_not_canon: + if mats_not_canon and mapping_set != "block_mapping_mineways": print("Non-canon material names found: ({})".format(len(mats_not_canon))) print(mats_not_canon) if len(mats_not_canon) > 30: # arbitrary threshold From 8f1dda45371e2e83b60d024922ef4f50c724bc96 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 15:32:21 -0700 Subject: [PATCH 100/103] Auto updated mapping json and version upgrade. --- .../MCprep_resources/mcprep_data_update.json | 68 +++++++++++++------ MCprep_addon/__init__.py | 2 +- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/MCprep_addon/MCprep_resources/mcprep_data_update.json b/MCprep_addon/MCprep_resources/mcprep_data_update.json index 8c818f4e..f41a84ad 100644 --- a/MCprep_addon/MCprep_resources/mcprep_data_update.json +++ b/MCprep_addon/MCprep_resources/mcprep_data_update.json @@ -856,7 +856,7 @@ "block_mapping_mc": { "Campfire": "campfire_log", "absorption": "mob_effect/absorption", - "acacia": "entity/chest_boat/acacia", + "acacia": "gui/hanging_signs/acacia", "acacia_door_bottom": "acacia_door_bottom", "acacia_door_top": "acacia_door_top", "acacia_leaves": "acacia_leaves", @@ -870,10 +870,10 @@ "accessibility": "gui/accessibility", "activator_rail": "activator_rail", "activator_rail_on": "activator_rail_on", - "adventure": null, + "adventure": "gui/advancements/backgrounds/adventure", "aggressive_panda": "entity/panda/aggressive_panda", "alban": "painting/alban", - "alex": "entity/alex", + "alex": "entity/player/wide/alex", "all_black": "entity/cat/all_black", "allay": "entity/allay/allay", "allium": "allium", @@ -885,6 +885,7 @@ "angry": "particle/angry", "anvil": "anvil", "anvil_top": "anvil_top", + "ari": "entity/player/wide/ari", "armorer": "entity/zombie_villager/profession/armorer", "arrow": "entity/projectiles/arrow", "ascii": "font/ascii", @@ -906,11 +907,23 @@ "azure_bluet": "azure_bluet", "back": "painting/back", "bad_omen": "mob_effect/bad_omen", + "bamboo": "gui/hanging_signs/bamboo", + "bamboo_block": "bamboo_block", + "bamboo_block_top": "bamboo_block_top", + "bamboo_door_bottom": "bamboo_door_bottom", + "bamboo_door_top": "bamboo_door_top", + "bamboo_fence": "bamboo_fence", + "bamboo_fence_gate": "bamboo_fence_gate", + "bamboo_fence_gate_particle": "bamboo_fence_gate_particle", + "bamboo_fence_particle": "bamboo_fence_particle", "bamboo_large_leaves": "bamboo_large_leaves", + "bamboo_mosaic": "bamboo_mosaic", + "bamboo_planks": "bamboo_planks", "bamboo_singleleaf": "bamboo_singleleaf", "bamboo_small_leaves": "bamboo_small_leaves", "bamboo_stage0": "bamboo_stage0", "bamboo_stalk": "bamboo_stalk", + "bamboo_trapdoor": "bamboo_trapdoor", "banner_base": "entity/banner_base", "barrel_bottom": "barrel_bottom", "barrel_side": "barrel_side", @@ -963,7 +976,7 @@ "big_smoke_7": "particle/big_smoke_7", "big_smoke_8": "particle/big_smoke_8", "big_smoke_9": "particle/big_smoke_9", - "birch": "entity/chest_boat/birch", + "birch": "gui/hanging_signs/birch", "birch_door_bottom": "birch_door_bottom", "birch_door_top": "birch_door_top", "birch_leaves": "birch_leaves", @@ -972,7 +985,7 @@ "birch_planks": "birch_planks", "birch_sapling": "birch_sapling", "birch_trapdoor": "birch_trapdoor", - "black": "entity/cat/black", + "black": "entity/rabbit/black", "black_candle": "black_candle", "black_candle_lit": "black_candle_lit", "black_concrete": "black_concrete", @@ -1019,7 +1032,7 @@ "brewing_stand_base": "brewing_stand_base", "bricks": "bricks", "british_shorthair": "entity/cat/british_shorthair", - "brown": "entity/bed/brown", + "brown": "entity/rabbit/brown", "brown_candle": "brown_candle", "brown_candle_lit": "brown_candle_lit", "brown_concrete": "brown_concrete", @@ -1059,6 +1072,7 @@ "cake_top": "cake_top", "calcite": "calcite", "calico": "entity/cat/calico", + "camel": "entity/camel/camel", "campfire_fire": "campfire_fire", "campfire_log": "campfire_log", "campfire_log_lit": "campfire_log_lit", @@ -1094,9 +1108,13 @@ "chainmail_layer_2": "models/armor/chainmail_layer_2", "chat_tags": "gui/chat_tags", "checkbox": "gui/checkbox", - "checkmark": null, + "checkmark": "gui/checkmark", "chicken": "entity/chicken", "chipped_anvil_top": "chipped_anvil_top", + "chiseled_bookshelf_empty": "chiseled_bookshelf_empty", + "chiseled_bookshelf_occupied": "chiseled_bookshelf_occupied", + "chiseled_bookshelf_side": "chiseled_bookshelf_side", + "chiseled_bookshelf_top": "chiseled_bookshelf_top", "chiseled_deepslate": "chiseled_deepslate", "chiseled_nether_bricks": "chiseled_nether_bricks", "chiseled_polished_blackstone": "chiseled_polished_blackstone", @@ -1159,7 +1177,7 @@ "creebet": "painting/creebet", "creeper": "entity/banner/creeper", "creeper_armor": "entity/creeper/creeper_armor", - "crimson": "entity/signs/crimson", + "crimson": "gui/hanging_signs/crimson", "crimson_door_bottom": "crimson_door_bottom", "crimson_door_top": "crimson_door_top", "crimson_fungus": "crimson_fungus", @@ -1194,7 +1212,7 @@ "damage": "particle/damage", "damaged_anvil_top": "damaged_anvil_top", "dandelion": "dandelion", - "dark_oak": "entity/chest_boat/dark_oak", + "dark_oak": "gui/hanging_signs/dark_oak", "dark_oak_door_bottom": "dark_oak_door_bottom", "dark_oak_door_top": "dark_oak_door_top", "dark_oak_leaves": "dark_oak_leaves", @@ -1292,6 +1310,7 @@ "drowned_outer_layer": "entity/zombie/drowned_outer_layer", "earth": "painting/earth", "edition": "gui/title/edition", + "efe": "entity/player/wide/efe", "effect_0": "particle/effect_0", "effect_1": "particle/effect_1", "effect_2": "particle/effect_2", @@ -1545,7 +1564,7 @@ "jukebox_side": "jukebox_side", "jukebox_top": "jukebox_top", "jump_boost": "mob_effect/jump_boost", - "jungle": "entity/chest_boat/jungle", + "jungle": "gui/hanging_signs/jungle", "jungle_door_bottom": "jungle_door_bottom", "jungle_door_top": "jungle_door_top", "jungle_leaves": "jungle_leaves", @@ -1554,6 +1573,7 @@ "jungle_planks": "jungle_planks", "jungle_sapling": "jungle_sapling", "jungle_trapdoor": "jungle_trapdoor", + "kai": "entity/player/wide/kai", "kebab": "painting/kebab", "kelp": "kelp", "kelp_plant": "kelp_plant", @@ -1642,7 +1662,8 @@ "magenta_wool": "magenta_wool", "magma": "magma", "magmacube": "entity/slime/magmacube", - "mangrove": "entity/chest_boat/mangrove", + "makena": "entity/player/wide/makena", + "mangrove": "gui/hanging_signs/mangrove", "mangrove_door_bottom": "mangrove_door_bottom", "mangrove_door_top": "mangrove_door_top", "mangrove_leaves": "mangrove_leaves", @@ -1703,12 +1724,13 @@ "night_vision": "mob_effect/night_vision", "nitwit": "entity/zombie_villager/profession/nitwit", "nonlatin_european": "font/nonlatin_european", + "noor": "entity/player/wide/noor", "normal": "entity/chest/normal", "normal_left": "entity/chest/normal_left", "normal_right": "entity/chest/normal_right", "note": "particle/note", "note_block": "note_block", - "oak": "entity/chest_boat/oak", + "oak": "gui/hanging_signs/oak", "oak_door_bottom": "oak_door_bottom", "oak_door_top": "oak_door_top", "oak_leaves": "oak_leaves", @@ -1771,7 +1793,7 @@ "phantom_eyes": "entity/phantom_eyes", "pig": "entity/pig/pig", "pig_saddle": "entity/pig/pig_saddle", - "piglin": "entity/piglin/piglin", + "piglin": "entity/banner/piglin", "piglin_brute": "entity/piglin/piglin_brute", "pigscene": "painting/pigscene", "pillager": "entity/illager/pillager", @@ -2040,7 +2062,7 @@ "shulker_yellow": "entity/shulker/shulker_yellow", "siamese": "entity/cat/siamese", "silverfish": "entity/silverfish", - "skeleton": "entity/skeleton/skeleton", + "skeleton": "painting/skeleton", "skull": "entity/banner/skull", "skull_and_roses": "painting/skull_and_roses", "slime": "entity/slime/slime", @@ -2140,7 +2162,7 @@ "sponge": "sponge", "spore_blossom": "spore_blossom", "spore_blossom_base": "spore_blossom_base", - "spruce": "entity/chest_boat/spruce", + "spruce": "gui/hanging_signs/spruce", "spruce_door_bottom": "spruce_door_bottom", "spruce_door_top": "spruce_door_top", "spruce_leaves": "spruce_leaves", @@ -2157,7 +2179,7 @@ "squid": "entity/squid/squid", "stage": "painting/stage", "stats_icons": "gui/container/stats_icons", - "steve": "entity/steve", + "steve": "entity/player/wide/steve", "stone": "stone", "stone_bricks": "stone_bricks", "stonecutter": "gui/container/stonecutter", @@ -2183,6 +2205,8 @@ "stripe_top": "entity/banner/stripe_top", "stripped_acacia_log": "stripped_acacia_log", "stripped_acacia_log_top": "stripped_acacia_log_top", + "stripped_bamboo_block": "stripped_bamboo_block", + "stripped_bamboo_block_top": "stripped_bamboo_block_top", "stripped_birch_log": "stripped_birch_log", "stripped_birch_log_top": "stripped_birch_log_top", "stripped_crimson_stem": "stripped_crimson_stem", @@ -2210,6 +2234,7 @@ "sunflower_bottom": "sunflower_bottom", "sunflower_front": "sunflower_front", "sunflower_top": "sunflower_top", + "sunny": "entity/player/wide/sunny", "sunset": "painting/sunset", "survival_spawn": null, "swamp": "entity/zombie_villager/type/swamp", @@ -2534,7 +2559,7 @@ "warden_pulsating_spots_1": "entity/warden/warden_pulsating_spots_1", "warden_pulsating_spots_2": "entity/warden/warden_pulsating_spots_2", "warm_frog": "entity/frog/warm_frog", - "warped": "entity/signs/warped", + "warped": "gui/hanging_signs/warped", "warped_door_bottom": "warped_door_bottom", "warped_door_top": "warped_door_top", "warped_fungus": "warped_fungus", @@ -2569,7 +2594,7 @@ "wheat_stage5": "wheat_stage5", "wheat_stage6": "wheat_stage6", "wheat_stage7": "wheat_stage7", - "white": "misc/white", + "white": "entity/rabbit/white", "white_candle": "white_candle", "white_candle_lit": "white_candle_lit", "white_concrete": "white_concrete", @@ -2583,11 +2608,11 @@ "white_tulip": "white_tulip", "white_wool": "white_wool", "widgets": "gui/widgets", - "wind": "entity/conduit/wind", + "wind": "painting/wind", "wind_vertical": "entity/conduit/wind_vertical", "window": "gui/advancements/window", "witch": "entity/witch", - "wither": "entity/wither/wither", + "wither": "painting/wither", "wither_armor": "entity/wither/wither_armor", "wither_invulnerable": "entity/wither/wither_invulnerable", "wither_rose": "wither_rose", @@ -2614,7 +2639,8 @@ "zoglin": "entity/hoglin/zoglin", "zombie": "entity/zombie/zombie", "zombie_villager": "entity/zombie_villager/zombie_villager", - "zombified_piglin": "entity/piglin/zombified_piglin" + "zombified_piglin": "entity/piglin/zombified_piglin", + "zuri": "entity/player/wide/zuri" }, "block_mapping_mineways": { "1": "", diff --git a/MCprep_addon/__init__.py b/MCprep_addon/__init__.py index b8683067..5738e0b6 100755 --- a/MCprep_addon/__init__.py +++ b/MCprep_addon/__init__.py @@ -41,7 +41,7 @@ bl_info = { "name": "MCprep", "category": "Object", - "version": (3, 4, 1, 1), + "version": (3, 4, 2), "blender": (2, 80, 0), "location": "3D window toolshelf > MCprep tab", "description": "Minecraft workflow addon for rendering and animation", From 856bb3403183003a69a867c0e8be3c495b042906 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 16:07:40 -0700 Subject: [PATCH 101/103] Minor stability fix to improve handling in blender 2.7x optimizer --- MCprep_addon/optimize_scene.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MCprep_addon/optimize_scene.py b/MCprep_addon/optimize_scene.py index f1619ae4..e09f11d7 100644 --- a/MCprep_addon/optimize_scene.py +++ b/MCprep_addon/optimize_scene.py @@ -185,8 +185,12 @@ def is_pricipled(self, context, mat_type, node): def execute(self, context): # ! Calling this twice seems to remove all unused materials # TODO: find a better way of doing this - bpy.ops.outliner.orphans_purge() - bpy.ops.outliner.orphans_purge() + try: + bpy.ops.outliner.orphans_purge() + bpy.ops.outliner.orphans_purge() + except Exception as e: + print("Failed to purge orphans:") + print(e) prefs = util.get_preferences(context) cprefs = prefs.addons.get("cycles") scn_props = context.scene.optimizer_props From 9914837a1c7f646f6b0cd9d19c8372493345d1b9 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 21:51:39 -0700 Subject: [PATCH 102/103] Fix saturation with old water world exports. Including override mapping of water -> water_still on user side, and json mapping of calling water texture as desaturated. --- MCprep_addon/MCprep_resources/mcprep_data_update.json | 5 +++++ MCprep_addon/materials/generate.py | 6 ++++++ mcprep_data_base.json | 1 + 3 files changed, 12 insertions(+) diff --git a/MCprep_addon/MCprep_resources/mcprep_data_update.json b/MCprep_addon/MCprep_resources/mcprep_data_update.json index f41a84ad..fd0c4b42 100644 --- a/MCprep_addon/MCprep_resources/mcprep_data_update.json +++ b/MCprep_addon/MCprep_resources/mcprep_data_update.json @@ -3755,6 +3755,11 @@ 0.614651, 0.089036 ], + "water": [ + 0.067324, + 0.135132, + 2 + ], "water_flow": [ 0.067324, 0.135132, diff --git a/MCprep_addon/materials/generate.py b/MCprep_addon/materials/generate.py index 15886f35..9021e599 100644 --- a/MCprep_addon/materials/generate.py +++ b/MCprep_addon/materials/generate.py @@ -78,6 +78,12 @@ def get_mc_canonical_name(name): else: jmc_prefix = False + # Patch naming to avoid issues. + if general_name == "water": + # Improves connection with older exports, without getting + # mixed up with the new "water": "painting/water" texture. + general_name = "water_still" + if general_name in conf.json_data["blocks"]["block_mapping_mc"]: canon = conf.json_data["blocks"]["block_mapping_mc"][general_name] form = "mc" if not jmc_prefix else "jmc2obj" diff --git a/mcprep_data_base.json b/mcprep_data_base.json index ef109465..5f00830b 100644 --- a/mcprep_data_base.json +++ b/mcprep_data_base.json @@ -59,6 +59,7 @@ "redstone_dust_line0": [0.3, 0, 0], "redstone_dust_line1": [0.3, 0, 0], + "water":[0.067324, 0.135132, 2], "water_still":[0.067324, 0.135132, 2], "water_flow":[0.067324, 0.135132, 2], "water_overlay":[0.067324, 0.135132, 2] From 1a49b947ca2664b21803f12dec29f3b694ecb4d5 Mon Sep 17 00:00:00 2001 From: "Patrick W. Crawford" Date: Sun, 12 Mar 2023 22:23:20 -0700 Subject: [PATCH 103/103] Fixed fake user setting in 2.7x for weather effects --- MCprep_addon/spawner/effects.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MCprep_addon/spawner/effects.py b/MCprep_addon/spawner/effects.py index e7e74e42..6acf45d3 100644 --- a/MCprep_addon/spawner/effects.py +++ b/MCprep_addon/spawner/effects.py @@ -138,8 +138,13 @@ def add_area_particle_effect(context, effect, location): imported_particles = list(set(post_systems) - set(pre_systems))[0] # Assign particles as fake, to avoid being purged after file reload. - if imported_particles.instance_object: - imported_particles.instance_object.use_fake_user = True + if hasattr(imported_particles, "instance_object"): + # 2.8+ + if imported_particles.instance_object: + imported_particles.instance_object.use_fake_user = True + elif imported_particles.dupli_object: + # the 2.7x way. + imported_particles.dupli_object.use_fake_user = True # Assign the active object and selection state. for sel_obj in bpy.context.selected_objects: