2025-12-01
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
def Diff(li1, li2):
|
||||
return (list(set(li1) - set(li2)))
|
||||
|
||||
def Intersection(li1, li2):
|
||||
set1 = set(li1)
|
||||
set2 = set(li2)
|
||||
return list(set.intersection(set1,set2))
|
||||
|
||||
def flatten(t):
|
||||
return [item for sublist in t for item in sublist]
|
||||
|
||||
def remove_duplicate(l):
|
||||
return list(dict.fromkeys(l))
|
||||
@@ -0,0 +1,39 @@
|
||||
import os
|
||||
import bpy
|
||||
class Node_Types:
|
||||
image_texture = 'TEX_IMAGE'
|
||||
pbr_node = 'BSDF_PRINCIPLED'
|
||||
mapping = 'MAPPING'
|
||||
normal_map = 'NORMAL_MAP'
|
||||
bump_map = 'BUMP'
|
||||
material_output = 'OUTPUT_MATERIAL'
|
||||
|
||||
class Shader_Node_Types:
|
||||
emission = "ShaderNodeEmission"
|
||||
image_texture = "ShaderNodeTexImage"
|
||||
mapping = "ShaderNodeMapping"
|
||||
normal = "ShaderNodeNormalMap"
|
||||
ao = "ShaderNodeAmbientOcclusion"
|
||||
uv = "ShaderNodeUVMap"
|
||||
comp_image_node = 'CompositorNodeImage'
|
||||
mix ="ShaderNodeMixRGB"
|
||||
|
||||
|
||||
class Bake_Passes:
|
||||
pbr = ["EMISSION"]
|
||||
lightmap = ["NOISY", "NRM", "COLOR"]
|
||||
ao = ["AO","COLOR"]
|
||||
|
||||
class Material_Suffix:
|
||||
bake_type_mat_suffix = {
|
||||
"pbr" : "_Bake",
|
||||
"ao" : "_AO",
|
||||
"lightmap" : "_AO"
|
||||
}
|
||||
class Path_List:
|
||||
def get_project_dir():
|
||||
return os.path.dirname(bpy.data.filepath)
|
||||
|
||||
def get_textures_dir():
|
||||
return os.path.join(os.path.dirname(bpy.data.filepath),'textures','GLBTexTool')
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
|
||||
def update_pbr_button(self,context):
|
||||
self["lightmap_bake"] = False
|
||||
self["ao_bake"] = False
|
||||
|
||||
def update_lightmap_button(self,context):
|
||||
self["pbr_bake"] = False
|
||||
self["ao_bake"] = False
|
||||
update_active_element_in_bake_list()
|
||||
|
||||
def update_ao_button(self,context):
|
||||
self["lightmap_bake"] = False
|
||||
self["pbr_bake"] = False
|
||||
update_active_element_in_bake_list()
|
||||
|
||||
|
||||
# ----------------------- UPDATE BAKE IMAGE NAME / ENUM--------------------#
|
||||
|
||||
last_selection = []
|
||||
|
||||
@persistent
|
||||
def update_on_selection(scene):
|
||||
C = bpy.context
|
||||
global last_selection
|
||||
object = getattr(C,"object",None)
|
||||
if object is None:
|
||||
return
|
||||
|
||||
if C.selected_objects != last_selection:
|
||||
last_selection = C.selected_objects
|
||||
update_active_element_in_bake_list()
|
||||
|
||||
def update_bake_list(bake_settings,context):
|
||||
|
||||
bake_textures_set = set()
|
||||
|
||||
for obj in bpy.data.objects:
|
||||
if bake_settings.lightmap_bake:
|
||||
if obj.get("lightmap_name"):
|
||||
bake_textures_set.add((obj.lightmap_name, obj.lightmap_name, "Baked Texture Name"))
|
||||
if bake_settings.ao_bake:
|
||||
if obj.get("ao_map_name"):
|
||||
bake_textures_set.add((obj.ao_map_name, obj.ao_map_name, "Baked Texture Name"))
|
||||
|
||||
if len(bake_textures_set) == 0:
|
||||
bake_textures_set.add(("-- Baking Groups --","-- Baking Groups --","No Lightmap baked yet"))
|
||||
|
||||
return list(bake_textures_set)
|
||||
|
||||
|
||||
def update_active_element_in_bake_list():
|
||||
C = bpy.context
|
||||
active_object = C.active_object
|
||||
bake_settings = C.scene.bake_settings
|
||||
new_bake_image_name = ""
|
||||
|
||||
if bake_settings.lightmap_bake:
|
||||
new_bake_image_name = active_object.get("lightmap_name")
|
||||
if new_bake_image_name is None:
|
||||
new_bake_image_name = "Lightmap " + active_object.name
|
||||
if bake_settings.ao_bake:
|
||||
new_bake_image_name = active_object.get("ao_map_name")
|
||||
if new_bake_image_name is None:
|
||||
new_bake_image_name = "AO " + active_object.name
|
||||
|
||||
enum_items = bake_settings.get_baked_lightmaps()
|
||||
keys = [key[0] for key in enum_items]
|
||||
if new_bake_image_name in keys:
|
||||
bake_settings.bake_image_name = new_bake_image_name
|
||||
bake_settings.baking_groups = new_bake_image_name
|
||||
else:
|
||||
if active_object.type == "MESH":
|
||||
bake_settings.bake_image_name = new_bake_image_name
|
||||
|
||||
|
||||
def headline(layout,*valueList):
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
|
||||
split = row.split()
|
||||
for pair in valueList:
|
||||
split = split.split(factor=pair[0])
|
||||
split.label(text=pair[1])
|
||||
|
||||
@persistent
|
||||
def init_values(self,context):
|
||||
bpy.context.scene.world.light_settings.distance = 1
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import bpy
|
||||
import os
|
||||
from . import node_functions
|
||||
from . import constants
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
@persistent
|
||||
def save_images(self,context):
|
||||
images = bpy.data.images
|
||||
for img in images:
|
||||
if img.is_dirty:
|
||||
print(img.name)
|
||||
save_image(img) # img.save()
|
||||
|
||||
|
||||
def get_all_images_in_ui_list():
|
||||
|
||||
images_in_scene = bpy.data.images
|
||||
image_name_list = bpy.types.GTT_TEX_UL_List.image_name_list
|
||||
images_found = []
|
||||
|
||||
if len(image_name_list) > 0:
|
||||
images_found = [img for img in images_in_scene for name_list_entry in image_name_list if img.name == name_list_entry]
|
||||
|
||||
return images_found
|
||||
|
||||
|
||||
def save_image(image,save_internally=False):
|
||||
|
||||
if save_internally:
|
||||
image.pack()
|
||||
else:
|
||||
filePath = bpy.data.filepath
|
||||
path = os.path.dirname(filePath)
|
||||
|
||||
if not os.path.exists(path + "/textures"):
|
||||
os.mkdir(path + "/textures")
|
||||
|
||||
if not os.path.exists(path + "/textures/GLBTexTool"):
|
||||
os.mkdir(path + "/textures/GLBTexTool")
|
||||
|
||||
if not os.path.exists(path + "/textures/GLBTexTool/" + str(image.size[0])):
|
||||
os.mkdir(path + "/textures/GLBTexTool/" + str(image.size[0]))
|
||||
|
||||
# file format
|
||||
image.file_format = bpy.context.scene.img_file_format
|
||||
|
||||
# change path
|
||||
savepath = path + "\\textures\\GLBTexTool\\" + str(image.size[0]) + "\\" + image.name + "." + image.file_format
|
||||
# image.use_fake_user = True
|
||||
image.filepath_raw = savepath
|
||||
image.save()
|
||||
|
||||
|
||||
|
||||
def create_image(image_name, image_size):
|
||||
D = bpy.data
|
||||
# find image
|
||||
image = D.images.get(image_name)
|
||||
|
||||
if image:
|
||||
old_size = list(image.size)
|
||||
new_size = list(image_size)
|
||||
|
||||
if old_size != new_size:
|
||||
D.images.remove(image)
|
||||
image = None
|
||||
|
||||
# image = D.images.get(image_name)
|
||||
|
||||
if image is None:
|
||||
image = D.images.new(
|
||||
image_name, width=image_size[0], height=image_size[1])
|
||||
image.name = image_name
|
||||
|
||||
return image
|
||||
|
||||
def get_file_size(filepath):
|
||||
size = "Unpack Files"
|
||||
try:
|
||||
path = bpy.path.abspath(filepath)
|
||||
size = os.path.getsize(path)
|
||||
size /= 1024
|
||||
except:
|
||||
return ("Unpack")
|
||||
# print("error getting file path for " + filepath)
|
||||
|
||||
return (size)
|
||||
|
||||
def scale_image(image, new_size):
|
||||
if (image.org_filepath != ''):
|
||||
image.filepath = image.org_filepath
|
||||
|
||||
image.org_filepath = image.filepath
|
||||
|
||||
if new_size[0] > image.size[0] or new_size[1] > image.size[1]:
|
||||
new_size[0] = image.size[0]
|
||||
new_size[1] = image.size[1]
|
||||
|
||||
# set image back to original if size is 0, else scale it
|
||||
if new_size[0] == 0:
|
||||
image.filepath_raw = image.org_filepath
|
||||
else:
|
||||
image.scale(new_size[0], new_size[1])
|
||||
save_image(image)
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import bpy
|
||||
def get_all_visible_materials():
|
||||
objects=[ob for ob in bpy.context.view_layer.objects if ob.visible_get()]
|
||||
slot_array = [object.material_slots for object in objects]
|
||||
vis_mat = set()
|
||||
for slots in slot_array:
|
||||
for slot in slots:
|
||||
vis_mat.add(slot.material)
|
||||
|
||||
# to remove None values in list
|
||||
vis_mat = list(filter(None, vis_mat))
|
||||
return vis_mat
|
||||
|
||||
def get_selected_materials(selected_objects):
|
||||
selected_materials = set()
|
||||
slots_array = [obj.material_slots for obj in selected_objects]
|
||||
for slots in slots_array:
|
||||
for slot in slots:
|
||||
selected_materials.add(slot.material)
|
||||
return selected_materials
|
||||
|
||||
def clean_empty_materials():
|
||||
for obj in bpy.data.objects:
|
||||
for slot in obj.material_slots:
|
||||
mat = slot.material
|
||||
if mat is None:
|
||||
print("Removed Empty Materials from " + obj.name)
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.material_slot_remove()
|
||||
|
||||
def clean_no_user_materials():
|
||||
for material in bpy.data.materials:
|
||||
if not material.users:
|
||||
bpy.data.materials.remove(material)
|
||||
|
||||
def use_nodes():
|
||||
for material in bpy.data.materials:
|
||||
if not material.use_nodes:
|
||||
material.use_nodes = True
|
||||
@@ -0,0 +1,390 @@
|
||||
import bpy.ops as O
|
||||
import bpy
|
||||
import os
|
||||
from .. Functions import constants
|
||||
import mathutils
|
||||
|
||||
|
||||
# -----------------------COMPOSITING--------------------#
|
||||
def blur_bake_image(noisy_image,color_image):
|
||||
|
||||
# switch on nodes and get reference
|
||||
if not bpy.context.scene.use_nodes:
|
||||
bpy.context.scene.use_nodes = True
|
||||
|
||||
tree = bpy.context.scene.node_tree
|
||||
|
||||
# add cam if not in scene
|
||||
cam = bpy.context.scene.camera
|
||||
if not cam:
|
||||
bpy.ops.object.camera_add()
|
||||
|
||||
# bake image
|
||||
image_node = tree.nodes.new(type='CompositorNodeImage')
|
||||
image_node.image = noisy_image
|
||||
image_node.location = 0, 0
|
||||
|
||||
# color image
|
||||
color_image_node = tree.nodes.new(type='CompositorNodeImage')
|
||||
color_image_node.image = color_image
|
||||
color_image_node.location = 0, 300
|
||||
|
||||
# create blur node
|
||||
blur_node = tree.nodes.new(type='CompositorNodeBilateralblur')
|
||||
blur_node.location = 300, 0
|
||||
|
||||
# create output node
|
||||
comp_node = tree.nodes.new('CompositorNodeComposite')
|
||||
comp_node.location = 600, 0
|
||||
|
||||
# link nodes
|
||||
links = tree.links
|
||||
links.new(image_node.outputs[0], blur_node.inputs[0])
|
||||
links.new(color_image_node.outputs[0], blur_node.inputs[1])
|
||||
links.new(blur_node.outputs[0], comp_node.inputs[0])
|
||||
|
||||
# set output resolution to image res
|
||||
bpy.context.scene.render.resolution_x = noisy_image.size[0]
|
||||
bpy.context.scene.render.resolution_y = noisy_image.size[1]
|
||||
|
||||
|
||||
# set output path
|
||||
scene = bpy.context.scene
|
||||
outputImagePath = constants.Path_List.get_textures_dir()
|
||||
|
||||
# set image format and quality
|
||||
scene.render.image_settings.file_format = bpy.context.scene.img_file_format
|
||||
scene.render.image_settings.quality = 100
|
||||
|
||||
scene.render.filepath = os.path.join(outputImagePath,noisy_image.name + "_Denoise_AO")
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
if bpy.context.scene.img_file_format == 'JPEG':
|
||||
file_extention = '.jpg'
|
||||
elif bpy.context.scene.img_file_format == 'PNG':
|
||||
file_extention = '.png'
|
||||
elif bpy.context.scene.img_file_format == 'HDR':
|
||||
file_extention = '.hdr'
|
||||
|
||||
# cleanup
|
||||
comp_nodes = [image_node,color_image_node,blur_node,comp_node]
|
||||
for node in comp_nodes:
|
||||
tree.nodes.remove(node)
|
||||
|
||||
return scene.render.filepath + file_extention
|
||||
|
||||
def comp_ai_denoise(noisy_image, nrm_image, color_image):
|
||||
|
||||
# switch on nodes and get reference
|
||||
if not bpy.context.scene.use_nodes:
|
||||
bpy.context.scene.use_nodes = True
|
||||
|
||||
tree = bpy.context.scene.node_tree
|
||||
|
||||
# add cam if not in scene
|
||||
cam = bpy.context.scene.camera
|
||||
if not cam:
|
||||
bpy.ops.object.camera_add()
|
||||
|
||||
# bake image
|
||||
image_node = tree.nodes.new(type='CompositorNodeImage')
|
||||
image_node.image = noisy_image
|
||||
image_node.location = 0, 0
|
||||
|
||||
# nrm image
|
||||
nrm_image_node = tree.nodes.new(type='CompositorNodeImage')
|
||||
nrm_image_node.image = nrm_image
|
||||
nrm_image_node.location = 0, 300
|
||||
|
||||
# color image
|
||||
color_image_node = tree.nodes.new(type='CompositorNodeImage')
|
||||
color_image_node.image = color_image
|
||||
color_image_node.location = 0, 600
|
||||
|
||||
# create denoise node
|
||||
denoise_node = tree.nodes.new(type='CompositorNodeDenoise')
|
||||
denoise_node.location = 300, 0
|
||||
|
||||
# create output node
|
||||
comp_node = tree.nodes.new('CompositorNodeComposite')
|
||||
comp_node.location = 600, 0
|
||||
|
||||
# link nodes
|
||||
links = tree.links
|
||||
links.new(image_node.outputs[0], denoise_node.inputs[0])
|
||||
links.new(nrm_image_node.outputs[0], denoise_node.inputs[1])
|
||||
links.new(color_image_node.outputs[0], denoise_node.inputs[2])
|
||||
links.new(denoise_node.outputs[0], comp_node.inputs[0])
|
||||
|
||||
# set output resolution to image res
|
||||
bpy.context.scene.render.resolution_x = noisy_image.size[0]
|
||||
bpy.context.scene.render.resolution_y = noisy_image.size[1]
|
||||
|
||||
# set output path
|
||||
scene = bpy.context.scene
|
||||
outputImagePath = constants.Path_List.get_textures_dir()
|
||||
|
||||
# set image format and quality
|
||||
scene.render.image_settings.file_format = bpy.context.scene.img_file_format
|
||||
scene.render.image_settings.quality = 100
|
||||
|
||||
scene.render.filepath = os.path.join(outputImagePath,noisy_image.name + "_Denoise_LM")
|
||||
print("Starting Denoise")
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
if bpy.context.scene.img_file_format == 'JPEG':
|
||||
file_extention = '.jpg'
|
||||
elif bpy.context.scene.img_file_format == 'PNG':
|
||||
file_extention = '.png'
|
||||
elif bpy.context.scene.img_file_format == 'HDR':
|
||||
file_extention = '.hdr'
|
||||
|
||||
# cleanup
|
||||
comp_nodes = [image_node, nrm_image_node,color_image_node, denoise_node, comp_node]
|
||||
for node in comp_nodes:
|
||||
tree.nodes.remove(node)
|
||||
|
||||
return scene.render.filepath + file_extention
|
||||
|
||||
|
||||
# -----------------------CHECKING --------------------#
|
||||
|
||||
|
||||
def check_pbr(self, material):
|
||||
check_ok = True
|
||||
|
||||
if material is None:
|
||||
return False
|
||||
|
||||
if material.node_tree is None:
|
||||
return False
|
||||
|
||||
if material.node_tree.nodes is None:
|
||||
return False
|
||||
|
||||
# get pbr shader
|
||||
nodes = material.node_tree.nodes
|
||||
pbr_node_type = constants.Node_Types.pbr_node
|
||||
pbr_nodes = get_nodes_by_type(nodes, pbr_node_type)
|
||||
|
||||
# check only one pbr node
|
||||
if len(pbr_nodes) == 0:
|
||||
self.report({'INFO'}, 'No PBR Shader Found')
|
||||
check_ok = False
|
||||
|
||||
if len(pbr_nodes) > 1:
|
||||
self.report(
|
||||
{'INFO'}, 'More than one PBR Node found ! Clean before Baking.')
|
||||
check_ok = False
|
||||
|
||||
return check_ok
|
||||
|
||||
|
||||
def check_is_org_material(self, material):
|
||||
check_ok = True
|
||||
if "_Bake" in material.name:
|
||||
self.report({'INFO'}, 'Change back to org. Material')
|
||||
check_ok = False
|
||||
|
||||
return check_ok
|
||||
|
||||
|
||||
# -----------------------NODES --------------------#
|
||||
|
||||
|
||||
def get_pbr_inputs(pbr_node):
|
||||
|
||||
base_color_input = pbr_node.inputs["Base Color"]
|
||||
metallic_input = pbr_node.inputs["Metallic"]
|
||||
specular_input = pbr_node.inputs["Specular Tint"]
|
||||
roughness_input = pbr_node.inputs["Roughness"]
|
||||
normal_input = pbr_node.inputs["Normal"]
|
||||
emission_input = pbr_node.inputs["Emission Color"]
|
||||
alpha_input = pbr_node.inputs["Alpha"]
|
||||
|
||||
pbr_inputs = {"base_color_input": base_color_input, "metallic_input": metallic_input,
|
||||
"specular_input": specular_input, "roughness_input": roughness_input,
|
||||
"normal_input": normal_input, "emission_input": emission_input,"alpha_input":alpha_input}
|
||||
return pbr_inputs
|
||||
|
||||
|
||||
def get_nodes_by_type(nodes, node_type):
|
||||
nodes_found = [n for n in nodes if n.type == node_type]
|
||||
return nodes_found
|
||||
|
||||
|
||||
def get_node_by_type_recusivly(material, note_to_start, node_type, del_nodes_inbetween=False):
|
||||
nodes = material.node_tree.nodes
|
||||
if note_to_start.type == node_type:
|
||||
return note_to_start
|
||||
|
||||
for input in note_to_start.inputs:
|
||||
for link in input.links:
|
||||
current_node = link.from_node
|
||||
if (del_nodes_inbetween and note_to_start.type != constants.Node_Types.normal_map and note_to_start.type != constants.Node_Types.bump_map):
|
||||
nodes.remove(note_to_start)
|
||||
return get_node_by_type_recusivly(material, current_node, node_type, del_nodes_inbetween)
|
||||
|
||||
|
||||
def get_node_by_name_recusivly(node, idname):
|
||||
if node.bl_idname == idname:
|
||||
return node
|
||||
|
||||
for input in node.inputs:
|
||||
for link in input.links:
|
||||
current_node = link.from_node
|
||||
return get_node_by_name_recusivly(current_node, idname)
|
||||
|
||||
|
||||
def get_pbr_node(material):
|
||||
nodes = material.node_tree.nodes
|
||||
pbr_node = get_nodes_by_type(nodes, constants.Node_Types.pbr_node)
|
||||
if len(pbr_node) > 0:
|
||||
return pbr_node[0]
|
||||
|
||||
|
||||
def make_link(material, socket1, socket2):
|
||||
links = material.node_tree.links
|
||||
links.new(socket1, socket2)
|
||||
|
||||
|
||||
def remove_link(material, socket1, socket2):
|
||||
|
||||
node_tree = material.node_tree
|
||||
links = node_tree.links
|
||||
|
||||
for l in socket1.links:
|
||||
if l.to_socket == socket2:
|
||||
links.remove(l)
|
||||
|
||||
|
||||
def add_in_gamme_node(material, pbrInput):
|
||||
nodeToPrincipledOutput = pbrInput.links[0].from_socket
|
||||
|
||||
gammaNode = material.node_tree.nodes.new("ShaderNodeGamma")
|
||||
gammaNode.inputs[1].default_value = 2.2
|
||||
gammaNode.name = "Gamma Bake"
|
||||
|
||||
# link in gamma
|
||||
make_link(material, nodeToPrincipledOutput, gammaNode.inputs["Color"])
|
||||
make_link(material, gammaNode.outputs["Color"], pbrInput)
|
||||
|
||||
|
||||
def remove_gamma_node(material, pbrInput):
|
||||
nodes = material.node_tree.nodes
|
||||
gammaNode = nodes.get("Gamma Bake")
|
||||
nodeToPrincipledOutput = gammaNode.inputs[0].links[0].from_socket
|
||||
|
||||
make_link(material, nodeToPrincipledOutput, pbrInput)
|
||||
material.node_tree.nodes.remove(gammaNode)
|
||||
|
||||
|
||||
def emission_setup(material, node_output):
|
||||
nodes = material.node_tree.nodes
|
||||
emission_node = add_node(
|
||||
material, constants.Shader_Node_Types.emission, "Emission Bake")
|
||||
|
||||
# link emission to whatever goes into current pbrInput
|
||||
emission_input = emission_node.inputs[0]
|
||||
make_link(material, node_output, emission_input)
|
||||
|
||||
# link emission to materialOutput
|
||||
surface_input = get_nodes_by_type(nodes,constants.Node_Types.material_output)[0].inputs[0]
|
||||
emission_output = emission_node.outputs[0]
|
||||
make_link(material, emission_output, surface_input)
|
||||
|
||||
|
||||
def link_pbr_to_output(material, pbr_node):
|
||||
nodes = material.node_tree.nodes
|
||||
surface_input = get_nodes_by_type(nodes,constants.Node_Types.material_output)[0].inputs[0]
|
||||
make_link(material, pbr_node.outputs[0], surface_input)
|
||||
|
||||
def reconnect_PBR(material, pbrNode):
|
||||
nodes = material.node_tree.nodes
|
||||
pbr_output = pbrNode.outputs[0]
|
||||
surface_input = get_nodes_by_type(
|
||||
nodes, constants.Node_Types.material_output)[0].inputs[0]
|
||||
make_link(material, pbr_output, surface_input)
|
||||
|
||||
|
||||
def mute_all_texture_mappings(material, do_mute):
|
||||
nodes = material.node_tree.nodes
|
||||
for node in nodes:
|
||||
if node.bl_idname == "ShaderNodeMapping":
|
||||
node.mute = do_mute
|
||||
|
||||
|
||||
def add_node(material, shader_node_type, node_name):
|
||||
nodes = material.node_tree.nodes
|
||||
new_node = nodes.get(node_name)
|
||||
if new_node is None:
|
||||
new_node = nodes.new(shader_node_type)
|
||||
new_node.name = node_name
|
||||
new_node.label = node_name
|
||||
return new_node
|
||||
|
||||
|
||||
def remove_node(material, node_name):
|
||||
nodes = material.node_tree.nodes
|
||||
node = nodes.get(node_name)
|
||||
if node is not None:
|
||||
nodes.remove(node)
|
||||
|
||||
def remove_reconnect_node(material, node_name):
|
||||
nodes = material.node_tree.nodes
|
||||
node = nodes.get(node_name)
|
||||
input_node = node.inputs["Color1"].links[0].from_node
|
||||
output_node = node.outputs["Color"].links[0].to_node
|
||||
|
||||
if node is not None:
|
||||
make_link(material,input_node.outputs["Color"],output_node.inputs["Base Color"])
|
||||
nodes.remove(node)
|
||||
|
||||
def remove_unused_nodes(material):
|
||||
nodes = material.node_tree.nodes
|
||||
all_nodes = set(nodes)
|
||||
connected_nodes = set()
|
||||
material_output = nodes.get("Material Output")
|
||||
|
||||
get_all_connected_nodes(material_output, connected_nodes)
|
||||
|
||||
unconnected_nodes = all_nodes - connected_nodes
|
||||
|
||||
for node in unconnected_nodes:
|
||||
nodes.remove(node)
|
||||
|
||||
|
||||
def remove_double_linking(material,texture_node):
|
||||
color_output = texture_node.outputs["Color"]
|
||||
links_count = len(color_output.links)
|
||||
org_vector_input = texture_node.inputs["Vector"]
|
||||
position_y = texture_node.location.y
|
||||
|
||||
if links_count > 1:
|
||||
for link in color_output.links:
|
||||
|
||||
new_texture_node = add_node(material,constants.Shader_Node_Types.image_texture,texture_node.name + "_Copy" + str(link))
|
||||
new_texture_node.image = texture_node.image
|
||||
new_texture_node.location = texture_node.location
|
||||
position_y -= 250
|
||||
new_texture_node.location.y = position_y
|
||||
|
||||
# relink tex node output
|
||||
make_link(material,new_texture_node.outputs["Color"],link.to_socket)
|
||||
|
||||
# remap texture mapping
|
||||
if len(org_vector_input.links) != 0:
|
||||
new_vector_input = new_texture_node.inputs["Vector"]
|
||||
tex_transform_socket = org_vector_input.links[0].from_socket
|
||||
make_link(material,tex_transform_socket,new_vector_input)
|
||||
|
||||
|
||||
|
||||
def get_all_connected_nodes(node, connected_nodes):
|
||||
|
||||
connected_nodes.add(node)
|
||||
|
||||
for input in node.inputs:
|
||||
for link in input.links:
|
||||
current_node = link.from_node
|
||||
get_all_connected_nodes(current_node, connected_nodes)
|
||||
@@ -0,0 +1,36 @@
|
||||
import bpy
|
||||
from .. Functions import node_functions
|
||||
|
||||
def apply_transform_on_linked():
|
||||
bpy.ops.object.select_linked(type='OBDATA')
|
||||
bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', object=True, obdata=True, material=False, animation=False)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
bpy.ops.object.make_links_data(type='OBDATA')
|
||||
|
||||
def select_object(self, obj):
|
||||
C = bpy.context
|
||||
O = bpy.ops
|
||||
try:
|
||||
O.object.select_all(action='DESELECT')
|
||||
C.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
except:
|
||||
self.report({'INFO'}, "Object not in View Layer")
|
||||
|
||||
def select_obj_by_mat(mat,self=None):
|
||||
D = bpy.data
|
||||
result = []
|
||||
for obj in D.objects:
|
||||
if obj.type == "MESH":
|
||||
object_materials = [slot.material for slot in obj.material_slots]
|
||||
if mat in object_materials:
|
||||
result.append(obj)
|
||||
if (self):
|
||||
select_object(self, obj)
|
||||
|
||||
return result
|
||||
|
||||
# TODO - save objects in array, unlink objects, apply scale and link them back
|
||||
def apply_scale_on_multiuser():
|
||||
O = bpy.ops
|
||||
O.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
@@ -0,0 +1,200 @@
|
||||
import bpy
|
||||
from bpy import context
|
||||
from . import node_functions
|
||||
from . import material_functions
|
||||
from . import object_functions
|
||||
from . import constants
|
||||
from . import basic_functions
|
||||
import mathutils
|
||||
|
||||
|
||||
def update_selected_image(self, context):
|
||||
sel_texture = bpy.data.images[self.texture_index]
|
||||
show_image_in_image_editor(sel_texture)
|
||||
|
||||
|
||||
def show_image_in_image_editor(image):
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'IMAGE_EDITOR':
|
||||
area.spaces.active.image = image
|
||||
|
||||
|
||||
def switch_baked_material(show_bake_material,affect):
|
||||
|
||||
current_bake_type = bpy.context.scene.bake_settings.get_current_bake_type()
|
||||
material_name_suffix = constants.Material_Suffix.bake_type_mat_suffix[current_bake_type]
|
||||
|
||||
# on what object to work
|
||||
if affect == 'active':
|
||||
objects = [bpy.context.active_object]
|
||||
elif affect == 'selected':
|
||||
objects = bpy.context.selected_editable_objects
|
||||
elif affect == 'visible':
|
||||
objects = [ob for ob in bpy.context.view_layer.objects if ob.visible_get()]
|
||||
elif affect == 'scene':
|
||||
objects = bpy.context.scene.objects
|
||||
elif affect == 'linked':
|
||||
objects = []
|
||||
selected_objects = bpy.context.selected_editable_objects
|
||||
selected_materials = material_functions.get_selected_materials(selected_objects)
|
||||
|
||||
for material in selected_materials:
|
||||
objs = object_functions.select_obj_by_mat(material)
|
||||
objects.append(objs)
|
||||
objects = basic_functions.flatten(objects)
|
||||
objects = basic_functions.remove_duplicate(objects)
|
||||
|
||||
|
||||
all_mats = bpy.data.materials
|
||||
baked_mats = [mat for mat in all_mats if material_name_suffix in mat.name]
|
||||
|
||||
|
||||
for obj in objects:
|
||||
# if current_bake_type != "pbr":
|
||||
# baked_ao_flag = getattr(obj,"ao_map_name") != '' or getattr(obj,"lightmap_name") != ''
|
||||
# if not baked_ao_flag:
|
||||
# continue
|
||||
|
||||
for slot in obj.material_slots:
|
||||
if show_bake_material:
|
||||
for baked_mat in baked_mats:
|
||||
try:
|
||||
if baked_mat.name == slot.material.name + material_name_suffix + obj.bake_version:
|
||||
slot.material = baked_mat
|
||||
except:
|
||||
pass
|
||||
|
||||
else:
|
||||
if (material_name_suffix in slot.material.name):
|
||||
bake_material = slot.material
|
||||
index = bake_material.name.find(material_name_suffix)
|
||||
org_mat = all_mats.get(bake_material.name[0:index])
|
||||
if org_mat is not None:
|
||||
slot.material = org_mat
|
||||
|
||||
def preview_bake_texture(self,context):
|
||||
context = bpy.context
|
||||
bake_settings = context.scene.bake_settings
|
||||
preview_bake_texture = context.scene.texture_settings.preview_bake_texture
|
||||
vis_mats = material_functions.get_all_visible_materials()
|
||||
for mat in vis_mats:
|
||||
if not mat.node_tree:
|
||||
continue
|
||||
|
||||
nodes = mat.node_tree.nodes
|
||||
bake_texture_node = None
|
||||
if bake_settings.lightmap_bake:
|
||||
bake_texture_node = nodes.get(bake_settings.texture_node_lightmap)
|
||||
|
||||
elif bake_settings.ao_bake:
|
||||
bake_texture_node = nodes.get(bake_settings.texture_node_ao)
|
||||
|
||||
|
||||
if bake_texture_node is not None:
|
||||
if preview_bake_texture:
|
||||
node_functions.emission_setup(mat, bake_texture_node.outputs["Color"])
|
||||
else:
|
||||
pbr_node = node_functions.get_nodes_by_type(nodes, constants.Node_Types.pbr_node)
|
||||
if len(pbr_node) == 0:
|
||||
return
|
||||
|
||||
pbr_node = pbr_node[0]
|
||||
node_functions.remove_node(mat, "Emission Bake")
|
||||
node_functions.reconnect_PBR(mat, pbr_node)
|
||||
|
||||
|
||||
def preview_lightmap(self, context):
|
||||
preview_lightmap = context.scene.texture_settings.preview_lightmap
|
||||
vis_mats = material_functions.get_all_visible_materials()
|
||||
for material in vis_mats:
|
||||
if not material.node_tree:
|
||||
continue
|
||||
|
||||
nodes = material.node_tree.nodes
|
||||
|
||||
lightmap_node = nodes.get("Lightmap")
|
||||
if lightmap_node is None:
|
||||
continue
|
||||
|
||||
pbr_node = node_functions.get_pbr_node(material)
|
||||
if pbr_node is None:
|
||||
print("\n " + material.name + " has no PBR Node \n")
|
||||
continue
|
||||
base_color_input = node_functions.get_pbr_inputs(pbr_node)["base_color_input"]
|
||||
emission_input = node_functions.get_pbr_inputs(pbr_node)["emission_input"]
|
||||
|
||||
lightmap_output = lightmap_node.outputs["Color"]
|
||||
|
||||
if preview_lightmap:
|
||||
|
||||
# add mix node
|
||||
mix_node_name = "Mulitply Lightmap"
|
||||
mix_node = node_functions.add_node(material,constants.Shader_Node_Types.mix, mix_node_name)
|
||||
mix_node.blend_type = 'MULTIPLY'
|
||||
mix_node.inputs[0].default_value = 1 # set factor to 1
|
||||
pos_offset = mathutils.Vector((-200, 200))
|
||||
mix_node.location = pbr_node.location + pos_offset
|
||||
|
||||
mix_node_input1 = mix_node.inputs["Color1"]
|
||||
mix_node_input2 = mix_node.inputs["Color2"]
|
||||
mix_node_output = mix_node.outputs["Color"]
|
||||
|
||||
# image texture in base color
|
||||
if base_color_input.is_linked:
|
||||
node_before_base_color = base_color_input.links[0].from_node
|
||||
if not node_before_base_color.name == mix_node_name:
|
||||
node_functions.make_link(material, node_before_base_color.outputs["Color"], mix_node_input1)
|
||||
node_functions.make_link(material, lightmap_output, mix_node_input2)
|
||||
node_functions.make_link(material, mix_node_output, base_color_input)
|
||||
else :
|
||||
mix_node_input1.default_value = base_color_input.default_value
|
||||
node_functions.make_link(material, lightmap_output, mix_node_input2)
|
||||
node_functions.make_link(material, mix_node_output, base_color_input)
|
||||
|
||||
node_functions.remove_link(material,lightmap_output,emission_input)
|
||||
|
||||
if not preview_lightmap:
|
||||
|
||||
# remove mix and reconnect base color
|
||||
|
||||
mix_node = nodes.get("Mulitply Lightmap")
|
||||
|
||||
if mix_node is not None:
|
||||
color_input_connections = len(mix_node.inputs["Color1"].links)
|
||||
|
||||
if (color_input_connections == 0):
|
||||
node_functions.remove_node(material,mix_node.name)
|
||||
else:
|
||||
node_functions.remove_reconnect_node(material,mix_node.name)
|
||||
|
||||
node_functions.link_pbr_to_output(material,pbr_node)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def lightmap_to_emission(self, context, connect):
|
||||
|
||||
vis_mats = material_functions.get_all_visible_materials()
|
||||
for material in vis_mats:
|
||||
if not material.node_tree:
|
||||
continue
|
||||
|
||||
nodes = material.node_tree.nodes
|
||||
|
||||
pbr_node = node_functions.get_pbr_node(material)
|
||||
lightmap_node = nodes.get("Lightmap")
|
||||
|
||||
if lightmap_node is None:
|
||||
continue
|
||||
|
||||
emission_input = node_functions.get_pbr_inputs(pbr_node)["emission_input"]
|
||||
lightmap_output = lightmap_node.outputs["Color"]
|
||||
|
||||
if connect:
|
||||
node_functions.make_link(material, lightmap_output, emission_input)
|
||||
else:
|
||||
node_functions.remove_link(material,lightmap_output,emission_input)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user