Megascans Livelink Blender Fix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ##### QUIXEL AB - MEGASCANS LIVELINK FOR BLENDER ##### | |
# | |
# The Megascans LiveLink plugin for Blender is an add-on that lets | |
# you instantly import assets with their shader setup with one click only. | |
# | |
# Because it relies on some of the latest 2.80 features, this plugin is currently | |
# only available for Blender 2.80 and forward. | |
# | |
# You are free to modify, add features or tweak this add-on as you see fit, and | |
# don't hesitate to send us some feedback if you've done something cool with it. | |
# | |
# ##### QUIXEL AB - MEGASCANS LIVELINK FOR BLENDER ##### | |
# version: 0.5b | |
import bpy, threading, os, time, json, socket | |
globals()['Megascans_DataSet'] = None | |
bl_info = { | |
"name": "Megascans LiveLink", | |
"description": "Connects Blender to Quixel Bridge for one-click imports with shader setup and geometry", | |
"author": "Quixel", | |
"version": (2, 0), | |
"blender": (2, 80, 0), | |
"location": "File > Import", | |
"warning": "", # used for warning icon and text in addons panel | |
"wiki_url": "https://docs.quixel.org/bridge/livelinks/blender/info_quickstart.html", | |
"tracker_url": "https://docs.quixel.org/bridge/livelinks/blender/info_quickstart#release_notes", | |
"support": "COMMUNITY", | |
"category": "Import-Export" | |
} | |
# MS_Init_ImportProcess is the main asset import class. | |
# This class is invoked whenever a new asset is set from Bridge. | |
class MS_Init_ImportProcess(): | |
def __init__(self): | |
# This initialization method create the data structure to process our assets | |
# later on in the initImportProcess method. The method loops on all assets | |
# that have been sent by Bridge. | |
print("Initialized import class...") | |
try: | |
# Check if there's any incoming data | |
if globals()['Megascans_DataSet'] != None: | |
self.json_Array = json.loads(globals()['Megascans_DataSet']) | |
# Start looping over each asset in the self.json_Array list | |
for js in self.json_Array: | |
self.json_data = js | |
self.selectedObjects = [] | |
self.IOR = 1.45 | |
self.assetType = self.json_data["type"] | |
self.assetPath = self.json_data["path"] | |
self.assetID = self.json_data["id"] | |
self.isMetal = bool(self.json_data["category"] == "Metal") | |
# Workflow setup. | |
self.isHighPoly = bool(self.json_data["activeLOD"] == "high") | |
self.RenderEngine = bpy.context.scene.render.engine.lower() # Get the current render engine. i.e. blender_eevee or cycles | |
self.Workflow = self.json_data.get('pbrWorkflow', 'specular') | |
self.DisplacementSetup = 'regular' | |
self.isCycles = bool(self.RenderEngine == 'cycles') | |
if (self.isCycles): | |
if(bpy.context.scene.cycles.feature_set == 'EXPERIMENTAL'): | |
self.DisplacementSetup = 'adaptive' | |
baseTextures = ["albedo", "diffuse", "displacement", "normal", "roughness", | |
"specular", "normalbump", "ao", "opacity", | |
"translucency", "gloss", "metalness", "bump", "fuzz"] | |
# Create a list of tuples of all the textures maps available. | |
# This tuple is composed of (textureFormat, textureMapType, texturePath) | |
self.textureList = [] | |
for obj in self.json_data["components"]: | |
if obj["type"] in baseTextures: | |
if(obj["type"] == "displacement") and self.DisplacementSetup == 'adaptive': | |
#Do the EXR displacement thingy. | |
if (obj["format"] != "exr"): | |
disp_path_recv = obj["path"] | |
k = disp_path_recv.rfind(obj["format"]) # k is the last index for where the file format starts in the string. | |
disp_path_exr = disp_path_recv[:k] + "exr" # we appead exr as file type to check if it exists. | |
if(os.path.isfile(disp_path_exr)): | |
self.textureList.append(("exr", obj["type"], disp_path_exr)) # if EXR displacement exists we can safely use it. | |
else: | |
self.textureList.append( (obj["format"], obj["type"], obj["path"])) | |
else: | |
self.textureList.append( (obj["format"], obj["type"], obj["path"])) | |
else: | |
self.textureList.append( (obj["format"], obj["type"], obj["path"]) ) | |
# Create a tuple list of all the 3d meshes available. | |
# This tuple is composed of (meshFormat, meshPath) | |
self.geometryList = [(obj["format"], obj["path"]) for obj in self.json_data["meshList"]] | |
# Create name of our asset. Multiple conditions are set here | |
# in order to make sure the asset actually has a name and that the name | |
# is short enough for us to use it. We compose a name with the ID otherwise. | |
if "name" in self.json_data.keys(): | |
self.assetName = self.json_data["name"].replace(" ", "_") | |
else: | |
self.assetName = os.path.basename(self.json_data["path"]).replace(" ", "_") | |
if len(self.assetName.split("_")) > 2: | |
self.assetName = "_".join(self.assetName.split("_")[:-1]) | |
self.materialName = self.assetName + '_' + self.assetID | |
# Initialize the import method to start building our shader and import our geometry | |
self.initImportProcess() | |
print("Imported asset from " + self.assetName + " Quixel Bridge") | |
except Exception as e: | |
print( "Megascans LiveLink Error initializing the import process. Error: ", str(e) ) | |
globals()['Megascans_DataSet'] = None | |
# this method is used to import the geometry and create the material setup. | |
def initImportProcess(self): | |
try: | |
if len(self.textureList) >= 1: | |
self.ImportGeometry() | |
mat = self.CreateMaterial() | |
self.ApplyMaterialToGeometry(self.selectedObjects,mat) | |
if self.assetType == "3d": | |
self.SetupMaterial3DAsset(mat) | |
elif self.assetType == "3dplant": | |
self.SetupMaterial3DPlant(mat) | |
else: | |
if self.isMetal: | |
self.SetupMaterial2DMetal(mat) | |
else: | |
self.SetupMaterial2D(mat) | |
except Exception as e: | |
print( "Megascans LiveLink Error while importing textures/geometry or setting up material. Error: ", str(e) ) | |
def ImportGeometry(self): | |
# Import geometry | |
if len(self.geometryList) >= 1: | |
for obj in self.geometryList: | |
meshPath = obj[1] | |
meshFormat = obj[0] | |
if meshFormat.lower() == "fbx": | |
bpy.ops.import_scene.fbx(filepath=meshPath) | |
# get selected objects | |
obj_objects = [ o for o in bpy.context.scene.objects if o.select_get() ] | |
self.selectedObjects += obj_objects | |
elif meshFormat.lower() == "obj": | |
bpy.ops.import_scene.obj(filepath=meshPath) | |
# get selected objects | |
obj_objects = [ o for o in bpy.context.scene.objects if o.select_get() ] | |
self.selectedObjects += obj_objects | |
elif meshFormat.lower() == "abc" and False: | |
bpy.ops.wm.alembic_import(filepath=meshPath, as_background_job=False) | |
# bpy.ops.import_scene.obj(filepath=meshPath) | |
# get selected objects | |
obj_objects = [ o for o in bpy.context.scene.objects if o.select_get() ] | |
self.selectedObjects += obj_objects | |
def CreateMaterial(self): | |
# Create material | |
mat = (bpy.data.materials.get( self.materialName ) or bpy.data.materials.new( self.materialName )) | |
mat.use_nodes = True | |
return mat | |
def ApplyMaterialToGeometry(self, geo_list, mat): | |
for obj in geo_list: | |
# assign material to obj | |
obj.active_material = mat | |
# def AddModifiersToGeomtry(self, geo_list, mat): | |
# for obj in geo_list: | |
# # assign material to obj | |
# bpy.ops.object.modifier_add(type='SOLIDIFY') | |
#Shader setups for different asset types | |
def SetupMaterial2D (self, mat): | |
print("Setting up material for 2D surface (non-metal)") | |
nodes = mat.node_tree.nodes | |
# Get a list of all available texture maps. item[1] returns the map type (albedo, normal, etc...). | |
maps_ = [item[1] for item in self.textureList] | |
parentName = "Principled BSDF" | |
materialOutputName = "Material Output" | |
colorSpaces = ["sRGB", "Non-Color"] | |
mat.node_tree.nodes[parentName].distribution = 'MULTI_GGX' | |
mat.node_tree.nodes[parentName].inputs[4].default_value = 1 if self.isMetal else 0 # Metallic value | |
mat.node_tree.nodes[parentName].inputs[14].default_value = self.IOR | |
if self.isCycles: | |
# Create mapping node. | |
mappingNode = nodes.new("ShaderNodeMapping") | |
mappingNode.location = (-1950, 0) | |
mappingNode.vector_type = 'TEXTURE' | |
# Create texture coordinate node. | |
texCoordNode = nodes.new("ShaderNodeTexCoord") | |
texCoordNode.location = (-2150, -200) | |
# Connect texCoordNode to the mappingNode | |
mat.node_tree.links.new(mappingNode.inputs[0], texCoordNode.outputs[0]) | |
use_diffuse = True | |
# Create the albedo x ao setup. | |
if "albedo" in maps_: | |
if "ao" in maps_: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
aoPath = [item[2] for item in self.textureList if item[1] == "ao"] | |
if len(albedoPath) >= 1 and len(aoPath) >= 1: | |
use_diffuse = False | |
#Add Color>MixRGB node, transform it in the node editor, change it's operation to Multiply and finally we colapse the node. | |
multiplyNode = nodes.new("ShaderNodeMixRGB") | |
multiplyNode.blend_type = 'MULTIPLY' | |
multiplyNode.location = (-250, 320) | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 460) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
#Import ao and ao node setup. | |
aoPath = aoPath[0].replace("\\", "/") | |
aoNode = nodes.new('ShaderNodeTexImage') | |
aoNode.location = (-640, 200) | |
aoNode.image = bpy.data.images.load(aoPath) | |
aoNode.show_texture = True | |
aoNode.image.colorspace_settings.name = colorSpaces[1] | |
# Conned albedo and ao node to the multiply node. | |
mat.node_tree.links.new(multiplyNode.inputs[1], albedoNode.outputs[0]) | |
mat.node_tree.links.new(multiplyNode.inputs[2], aoNode.outputs[0]) | |
# Connect multiply node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], multiplyNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(albedoNode.inputs[0], mappingNode.outputs[0]) | |
mat.node_tree.links.new(aoNode.inputs[0], mappingNode.outputs[0]) | |
else: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
if len(albedoPath) >= 1: | |
use_diffuse = False | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 420) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect albedo node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], albedoNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(albedoNode.inputs[0], mappingNode.outputs[0]) | |
# Create the diffuse setup. | |
if "diffuse" in maps_ and use_diffuse: | |
diffusePath = [item[2] for item in self.textureList if item[1] == "diffuse"] | |
if len(diffusePath) >= 1: | |
# Import diffuse and diffuse node setup. | |
diffusePath = diffusePath[0].replace("\\", "/") | |
diffuseNode = nodes.new('ShaderNodeTexImage') | |
diffuseNode.location = (-640, 420) | |
diffuseNode.image = bpy.data.images.load(diffusePath) | |
diffuseNode.show_texture = True | |
diffuseNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect diffuse node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], diffuseNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(diffuseNode.inputs[0], mappingNode.outputs[0]) | |
use_metalness = True | |
# Create the specular setup. | |
if "specular" in maps_: | |
specularPath = [item[2] for item in self.textureList if item[1] == "specular"] | |
if len(specularPath) >= 1: | |
use_metalness = False | |
# Import specular and specular node setup. | |
specularPath = specularPath[0].replace("\\", "/") | |
specularNode = nodes.new('ShaderNodeTexImage') | |
specularNode.location = (-1150, 200) | |
specularNode.image = bpy.data.images.load(specularPath) | |
specularNode.show_texture = True | |
specularNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[5], specularNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(specularNode.inputs[0], mappingNode.outputs[0]) | |
# Create the metalness setup. Use metalness if specular is missing | |
if "metalness" in maps_ and use_metalness: | |
metalnessPath = [item[2] for item in self.textureList if item[1] == "metalness"] | |
if len(metalnessPath) >= 1: | |
# Import specular and specular node setup. | |
metalnessPath = metalnessPath[0].replace("\\", "/") | |
metalnessNode = nodes.new('ShaderNodeTexImage') | |
metalnessNode.location = (-1150, 200) | |
metalnessNode.image = bpy.data.images.load(metalnessPath) | |
metalnessNode.show_texture = True | |
metalnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[4], metalnessNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(metalnessNode.inputs[0], mappingNode.outputs[0]) | |
use_gloss = True | |
# Create the roughness setup. | |
if "roughness" in maps_: | |
roughnessPath = [item[2] for item in self.textureList if item[1] == "roughness"] | |
if len(roughnessPath) >= 1: | |
use_gloss = False | |
# Import roughness and roughness node setup. | |
roughnessPath = roughnessPath[0].replace("\\", "/") | |
roughnessNode = nodes.new('ShaderNodeTexImage') | |
roughnessNode.location = (-1150, -60) | |
roughnessNode.image = bpy.data.images.load(roughnessPath) | |
roughnessNode.show_texture = True | |
roughnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], roughnessNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(roughnessNode.inputs[0], mappingNode.outputs[0]) | |
# Create the gloss setup. | |
if "gloss" in maps_ and use_gloss: | |
glossPath = [item[2] for item in self.textureList if item[1] == "gloss"] | |
if len(glossPath) >= 1: | |
# Add vector>bump node | |
invertNode = nodes.new("ShaderNodeInvert") | |
invertNode.location = (-250, 68) | |
# Import roughness and roughness node setup. | |
glossPath = glossPath[0].replace("\\", "/") | |
glossNode = nodes.new('ShaderNodeTexImage') | |
glossNode.location = (-1150, -60) | |
glossNode.image = bpy.data.images.load(glossPath) | |
glossNode.show_texture = True | |
glossNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add glossNode to invertNode connection | |
mat.node_tree.links.new(invertNode.inputs[1], glossNode.outputs[0]) | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], invertNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(glossNode.inputs[0], mappingNode.outputs[0]) | |
# Create the opacity setup. | |
if "opacity" in maps_: | |
opacityPath = [item[2] for item in self.textureList if item[1] == "opacity"] | |
if len(opacityPath) >= 1: | |
# Import opacity and opacity node setup. | |
opacityPath = opacityPath[0].replace("\\", "/") | |
opacityNode = nodes.new('ShaderNodeTexImage') | |
opacityNode.location = (-1550, -160) | |
opacityNode.image = bpy.data.images.load(opacityPath) | |
opacityNode.show_texture = True | |
opacityNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect opacity node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[18], opacityNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(opacityNode.inputs[0], mappingNode.outputs[0]) | |
# Create the translucency setup. | |
if "translucency" in maps_: | |
translucencyPath = [item[2] for item in self.textureList if item[1] == "translucency"] | |
if len(translucencyPath) >= 1: | |
# Import translucency and translucency node setup. | |
translucencyPath = translucencyPath[0].replace("\\", "/") | |
translucencyNode = nodes.new('ShaderNodeTexImage') | |
translucencyNode.location = (-1550, -420) | |
translucencyNode.image = bpy.data.images.load(translucencyPath) | |
translucencyNode.show_texture = True | |
translucencyNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect translucency node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[15], translucencyNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(translucencyNode.inputs[0], mappingNode.outputs[0]) | |
setup_normal = True | |
# Create the normal + bump setup. | |
if "normal" in maps_ and "bump" in maps_: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(normalPath) >= 1 and len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -130) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-640, -400) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-1150, -580) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
mat.node_tree.links.new(bumpNode.inputs[5], normalNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(normalMapNode.inputs[0], mappingNode.outputs[0]) | |
mat.node_tree.links.new(bumpMapNode.inputs[0], mappingNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "normal" in maps_ and setup_normal: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
if len(normalPath) >= 1: | |
setup_normal = False | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-250, -170) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-640, -207) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add normalNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], normalNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(normalMapNode.inputs[0], mappingNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "bump" in maps_ and setup_normal: | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -207) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(bumpMapNode.inputs[0], mappingNode.outputs[0]) | |
# Create the displacement setup. | |
if "displacement" in maps_: | |
if self.DisplacementSetup == "adaptive": | |
displacementPath = [item[2] for item in self.textureList if item[1] == "displacement"] | |
if len(displacementPath) >= 1: | |
# Add vector>displacement map node | |
displacementNode = nodes.new("ShaderNodeDisplacement") | |
displacementNode.location = (10, -400) | |
displacementNode.inputs[0].default_value = 0.1 | |
displacementNode.inputs[1].default_value = 0 | |
# Add converter>RGB Separator node | |
RGBSplitterNode = nodes.new("ShaderNodeSeparateRGB") | |
RGBSplitterNode.location = (-250, -499) | |
# Import normal map and normal map node setup. | |
displacementPath = displacementPath[0].replace("\\", "/") | |
displacementMapNode = nodes.new('ShaderNodeTexImage') | |
displacementMapNode.location = (-640, -740) | |
displacementMapNode.image = bpy.data.images.load(displacementPath) | |
displacementMapNode.show_texture = True | |
displacementMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add displacementMapNode to RGBSplitterNode connection | |
mat.node_tree.links.new(RGBSplitterNode.inputs[0], displacementMapNode.outputs[0]) | |
# Add RGBSplitterNode to displacementNode connection | |
mat.node_tree.links.new(displacementNode.inputs[2], RGBSplitterNode.outputs[0]) | |
# Add normalNode connection to the material output displacement node | |
mat.node_tree.links.new(nodes.get(materialOutputName).inputs[2], displacementNode.outputs[0]) | |
mat.cycles.displacement_method = 'BOTH' | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(displacementMapNode.inputs[0], mappingNode.outputs[0]) | |
if self.DisplacementSetup == "regular": | |
pass | |
return | |
def SetupMaterial2DMetal (self, mat): | |
print("Setting up material for 2D surface (metal)") | |
print("Setting up material for 2D surface (non-metal)") | |
nodes = mat.node_tree.nodes | |
# Get a list of all available texture maps. item[1] returns the map type (albedo, normal, etc...). | |
maps_ = [item[1] for item in self.textureList] | |
parentName = "Principled BSDF" | |
materialOutputName = "Material Output" | |
colorSpaces = ["sRGB", "Non-Color"] | |
mat.node_tree.nodes[parentName].distribution = 'MULTI_GGX' | |
mat.node_tree.nodes[parentName].inputs[4].default_value = 1 if self.isMetal else 0 # Metallic value | |
mat.node_tree.nodes[parentName].inputs[14].default_value = self.IOR | |
if self.isCycles: | |
# Create mapping node. | |
mappingNode = nodes.new("ShaderNodeMapping") | |
mappingNode.location = (-1950, 0) | |
mappingNode.vector_type = 'TEXTURE' | |
# Create texture coordinate node. | |
texCoordNode = nodes.new("ShaderNodeTexCoord") | |
texCoordNode.location = (-2150, -200) | |
# Connect texCoordNode to the mappingNode | |
mat.node_tree.links.new(mappingNode.inputs[0], texCoordNode.outputs[0]) | |
use_diffuse = True | |
# Create the albedo x ao setup. | |
if "albedo" in maps_: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
if len(albedoPath) >= 1: | |
use_diffuse = False | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 420) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect albedo node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], albedoNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(albedoNode.inputs[0], mappingNode.outputs[0]) | |
use_metalness = True | |
# Create the diffuse setup. | |
if "diffuse" in maps_ and use_diffuse: | |
diffusePath = [item[2] for item in self.textureList if item[1] == "diffuse"] | |
if len(diffusePath) >= 1: | |
use_metalness = False or "specular" not in maps_ | |
# Import diffuse and diffuse node setup. | |
diffusePath = diffusePath[0].replace("\\", "/") | |
diffuseNode = nodes.new('ShaderNodeTexImage') | |
diffuseNode.location = (-640, 420) | |
diffuseNode.image = bpy.data.images.load(diffusePath) | |
diffuseNode.show_texture = True | |
diffuseNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect diffuse node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], diffuseNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(diffuseNode.inputs[0], mappingNode.outputs[0]) | |
use_specular = True | |
# Create the metalness setup. Use metalness if specular is missing | |
if "metalness" in maps_ and use_metalness: | |
metalnessPath = [item[2] for item in self.textureList if item[1] == "metalness"] | |
if len(metalnessPath) >= 1: | |
use_specular = False | |
# Import specular and specular node setup. | |
metalnessPath = metalnessPath[0].replace("\\", "/") | |
metalnessNode = nodes.new('ShaderNodeTexImage') | |
metalnessNode.location = (-1150, 200) | |
metalnessNode.image = bpy.data.images.load(metalnessPath) | |
metalnessNode.show_texture = True | |
metalnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[4], metalnessNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(metalnessNode.inputs[0], mappingNode.outputs[0]) | |
# Create the specular setup. | |
if "specular" in maps_ and use_specular: | |
specularPath = [item[2] for item in self.textureList if item[1] == "specular"] | |
if len(specularPath) >= 1: | |
use_metalness = False | |
# Import specular and specular node setup. | |
specularPath = specularPath[0].replace("\\", "/") | |
specularNode = nodes.new('ShaderNodeTexImage') | |
specularNode.location = (-1150, 200) | |
specularNode.image = bpy.data.images.load(specularPath) | |
specularNode.show_texture = True | |
specularNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[5], specularNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(specularNode.inputs[0], mappingNode.outputs[0]) | |
use_gloss = True | |
# Create the roughness setup. | |
if "roughness" in maps_: | |
roughnessPath = [item[2] for item in self.textureList if item[1] == "roughness"] | |
if len(roughnessPath) >= 1: | |
use_gloss = False | |
# Import roughness and roughness node setup. | |
roughnessPath = roughnessPath[0].replace("\\", "/") | |
roughnessNode = nodes.new('ShaderNodeTexImage') | |
roughnessNode.location = (-1150, -60) | |
roughnessNode.image = bpy.data.images.load(roughnessPath) | |
roughnessNode.show_texture = True | |
roughnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], roughnessNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(roughnessNode.inputs[0], mappingNode.outputs[0]) | |
# Create the gloss setup. | |
if "gloss" in maps_ and use_gloss: | |
glossPath = [item[2] for item in self.textureList if item[1] == "gloss"] | |
if len(glossPath) >= 1: | |
# Add vector>bump node | |
invertNode = nodes.new("ShaderNodeInvert") | |
invertNode.location = (-250, 68) | |
# Import roughness and roughness node setup. | |
glossPath = glossPath[0].replace("\\", "/") | |
glossNode = nodes.new('ShaderNodeTexImage') | |
glossNode.location = (-1150, -60) | |
glossNode.image = bpy.data.images.load(glossPath) | |
glossNode.show_texture = True | |
glossNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add glossNode to invertNode connection | |
mat.node_tree.links.new(invertNode.inputs[1], glossNode.outputs[0]) | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], invertNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(glossNode.inputs[0], mappingNode.outputs[0]) | |
setup_normal = True | |
# Create the normal + bump setup. | |
if "normal" in maps_ and "bump" in maps_: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(normalPath) >= 1 and len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -130) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-640, -400) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-1150, -580) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
mat.node_tree.links.new(bumpNode.inputs[5], normalNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(normalMapNode.inputs[0], mappingNode.outputs[0]) | |
mat.node_tree.links.new(bumpMapNode.inputs[0], mappingNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "normal" in maps_ and setup_normal: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
if len(normalPath) >= 1: | |
setup_normal = False | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-250, -170) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-640, -207) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add normalNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], normalNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(normalMapNode.inputs[0], mappingNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "bump" in maps_ and setup_normal: | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -207) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(bumpMapNode.inputs[0], mappingNode.outputs[0]) | |
# Create the displacement setup. | |
if "displacement" in maps_: | |
if self.DisplacementSetup == "adaptive": | |
displacementPath = [item[2] for item in self.textureList if item[1] == "displacement"] | |
if len(displacementPath) >= 1: | |
# Add vector>displacement map node | |
displacementNode = nodes.new("ShaderNodeDisplacement") | |
displacementNode.location = (10, -400) | |
displacementNode.inputs[0].default_value = 0.1 | |
displacementNode.inputs[1].default_value = 0 | |
# Add converter>RGB Separator node | |
RGBSplitterNode = nodes.new("ShaderNodeSeparateRGB") | |
RGBSplitterNode.location = (-250, -499) | |
# Import normal map and normal map node setup. | |
displacementPath = displacementPath[0].replace("\\", "/") | |
displacementMapNode = nodes.new('ShaderNodeTexImage') | |
displacementMapNode.location = (-640, -740) | |
displacementMapNode.image = bpy.data.images.load(displacementPath) | |
displacementMapNode.show_texture = True | |
displacementMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add displacementMapNode to RGBSplitterNode connection | |
mat.node_tree.links.new(RGBSplitterNode.inputs[0], displacementMapNode.outputs[0]) | |
# Add RGBSplitterNode to displacementNode connection | |
mat.node_tree.links.new(displacementNode.inputs[2], RGBSplitterNode.outputs[0]) | |
# Add normalNode connection to the material output displacement node | |
mat.node_tree.links.new(nodes.get(materialOutputName).inputs[2], displacementNode.outputs[0]) | |
mat.cycles.displacement_method = 'BOTH' | |
# Add mapping node connection in order to support tiling | |
if self.isCycles: | |
mat.node_tree.links.new(displacementMapNode.inputs[0], mappingNode.outputs[0]) | |
if self.DisplacementSetup == "regular": | |
pass | |
return | |
def SetupMaterial3DAsset (self, mat): | |
print("Setting up material for 3D Asset.") | |
nodes = mat.node_tree.nodes | |
# Get a list of all available texture maps. item[1] returns the map type (albedo, normal, etc...). | |
maps_ = [item[1] for item in self.textureList] | |
parentName = "Principled BSDF" | |
materialOutputName = "Material Output" | |
colorSpaces = ["sRGB", "Non-Color"] | |
mat.node_tree.nodes[parentName].distribution = 'MULTI_GGX' | |
mat.node_tree.nodes[parentName].inputs[4].default_value = 1 if self.isMetal else 0 # Metallic value | |
mat.node_tree.nodes[parentName].inputs[14].default_value = self.IOR | |
use_diffuse = True | |
# Create the albedo x ao setup. | |
if "albedo" in maps_: | |
if "ao" in maps_: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
aoPath = [item[2] for item in self.textureList if item[1] == "ao"] | |
if len(albedoPath) >= 1 and len(aoPath) >= 1: | |
use_diffuse = False | |
#Add Color>MixRGB node, transform it in the node editor, change it's operation to Multiply and finally we colapse the node. | |
multiplyNode = nodes.new("ShaderNodeMixRGB") | |
multiplyNode.blend_type = 'MULTIPLY' | |
multiplyNode.location = (-250, 320) | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 460) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
#Import ao and ao node setup. | |
aoPath = aoPath[0].replace("\\", "/") | |
aoNode = nodes.new('ShaderNodeTexImage') | |
aoNode.location = (-640, 200) | |
aoNode.image = bpy.data.images.load(aoPath) | |
aoNode.show_texture = True | |
aoNode.image.colorspace_settings.name = colorSpaces[1] | |
# Conned albedo and ao node to the multiply node. | |
mat.node_tree.links.new(multiplyNode.inputs[1], albedoNode.outputs[0]) | |
mat.node_tree.links.new(multiplyNode.inputs[2], aoNode.outputs[0]) | |
# Connect multiply node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], multiplyNode.outputs[0]) | |
else: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
if len(albedoPath) >= 1: | |
use_diffuse = False | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 420) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect albedo node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], albedoNode.outputs[0]) | |
# Create the diffuse setup. | |
if "diffuse" in maps_ and use_diffuse: | |
diffusePath = [item[2] for item in self.textureList if item[1] == "diffuse"] | |
if len(diffusePath) >= 1: | |
# Import diffuse and diffuse node setup. | |
diffusePath = diffusePath[0].replace("\\", "/") | |
diffuseNode = nodes.new('ShaderNodeTexImage') | |
diffuseNode.location = (-640, 420) | |
diffuseNode.image = bpy.data.images.load(diffusePath) | |
diffuseNode.show_texture = True | |
diffuseNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect diffuse node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], diffuseNode.outputs[0]) | |
use_metalness = True | |
# Create the specular setup. | |
if "specular" in maps_: | |
specularPath = [item[2] for item in self.textureList if item[1] == "specular"] | |
if len(specularPath) >= 1: | |
use_metalness = False | |
# Import specular and specular node setup. | |
specularPath = specularPath[0].replace("\\", "/") | |
specularNode = nodes.new('ShaderNodeTexImage') | |
specularNode.location = (-1150, 200) | |
specularNode.image = bpy.data.images.load(specularPath) | |
specularNode.show_texture = True | |
specularNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[5], specularNode.outputs[0]) | |
# Create the metalness setup. Use metalness if specular is missing | |
if "metalness" in maps_ and use_metalness: | |
metalnessPath = [item[2] for item in self.textureList if item[1] == "metalness"] | |
if len(metalnessPath) >= 1: | |
# Import specular and specular node setup. | |
metalnessPath = metalnessPath[0].replace("\\", "/") | |
metalnessNode = nodes.new('ShaderNodeTexImage') | |
metalnessNode.location = (-1150, 200) | |
metalnessNode.image = bpy.data.images.load(metalnessPath) | |
metalnessNode.show_texture = True | |
metalnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[4], metalnessNode.outputs[0]) | |
use_gloss = True | |
# Create the roughness setup. | |
if "roughness" in maps_: | |
roughnessPath = [item[2] for item in self.textureList if item[1] == "roughness"] | |
if len(roughnessPath) >= 1: | |
use_gloss = False | |
# Import roughness and roughness node setup. | |
roughnessPath = roughnessPath[0].replace("\\", "/") | |
roughnessNode = nodes.new('ShaderNodeTexImage') | |
roughnessNode.location = (-1150, -60) | |
roughnessNode.image = bpy.data.images.load(roughnessPath) | |
roughnessNode.show_texture = True | |
roughnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], roughnessNode.outputs[0]) | |
# Create the gloss setup. | |
if "gloss" in maps_ and use_gloss: | |
glossPath = [item[2] for item in self.textureList if item[1] == "gloss"] | |
if len(glossPath) >= 1: | |
# Add vector>bump node | |
invertNode = nodes.new("ShaderNodeInvert") | |
invertNode.location = (-250, 68) | |
# Import roughness and roughness node setup. | |
glossPath = glossPath[0].replace("\\", "/") | |
glossNode = nodes.new('ShaderNodeTexImage') | |
glossNode.location = (-1150, -60) | |
glossNode.image = bpy.data.images.load(glossPath) | |
glossNode.show_texture = True | |
glossNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add glossNode to invertNode connection | |
mat.node_tree.links.new(invertNode.inputs[1], glossNode.outputs[0]) | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], invertNode.outputs[0]) | |
# If HIGH POLY selected > use normal_bump and no displacement | |
# If LODs selected > use corresponding LODs normal + displacement | |
if self.isHighPoly: | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "normal" in maps_: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
if len(normalPath) >= 1: | |
setup_normal = False | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-250, -170) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-640, -207) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add normalNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], normalNode.outputs[0]) | |
else: | |
setup_normal = True | |
# Create the normal + bump setup. | |
if "normal" in maps_ and "bump" in maps_: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(normalPath) >= 1 and len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -130) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-640, -400) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-1150, -580) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
mat.node_tree.links.new(bumpNode.inputs[5], normalNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "normal" in maps_ and setup_normal: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
if len(normalPath) >= 1: | |
setup_normal = False | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-250, -170) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-640, -207) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add normalNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], normalNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "bump" in maps_ and setup_normal: | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -207) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Create the displacement setup. | |
if "displacement" in maps_: | |
if self.DisplacementSetup == "adaptive": | |
displacementPath = [item[2] for item in self.textureList if item[1] == "displacement"] | |
if len(displacementPath) >= 1: | |
# Add vector>displacement map node | |
displacementNode = nodes.new("ShaderNodeDisplacement") | |
displacementNode.location = (10, -400) | |
displacementNode.inputs[0].default_value = 0.1 | |
displacementNode.inputs[1].default_value = 0 | |
# Add converter>RGB Separator node | |
RGBSplitterNode = nodes.new("ShaderNodeSeparateRGB") | |
RGBSplitterNode.location = (-250, -499) | |
# Import normal map and normal map node setup. | |
displacementPath = displacementPath[0].replace("\\", "/") | |
displacementMapNode = nodes.new('ShaderNodeTexImage') | |
displacementMapNode.location = (-640, -740) | |
displacementMapNode.image = bpy.data.images.load(displacementPath) | |
displacementMapNode.show_texture = True | |
displacementMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add displacementMapNode to RGBSplitterNode connection | |
mat.node_tree.links.new(RGBSplitterNode.inputs[0], displacementMapNode.outputs[0]) | |
# Add RGBSplitterNode to displacementNode connection | |
mat.node_tree.links.new(displacementNode.inputs[2], RGBSplitterNode.outputs[0]) | |
# Add normalNode connection to the material output displacement node | |
mat.node_tree.links.new(nodes.get(materialOutputName).inputs[2], displacementNode.outputs[0]) | |
mat.cycles.displacement_method = 'BOTH' | |
if self.DisplacementSetup == "regular": | |
pass | |
return | |
def SetupMaterial3DPlant (self, mat): | |
print("Setting up material for 3D Plant.") | |
nodes = mat.node_tree.nodes | |
# Get a list of all available texture maps. item[1] returns the map type (albedo, normal, etc...). | |
maps_ = [item[1] for item in self.textureList] | |
parentName = "Principled BSDF" | |
materialOutputName = "Material Output" | |
colorSpaces = ["sRGB", "Non-Color"] | |
mat.node_tree.nodes[parentName].distribution = 'MULTI_GGX' | |
mat.node_tree.nodes[parentName].inputs[4].default_value = 1 if self.isMetal else 0 # Metallic value | |
mat.node_tree.nodes[parentName].inputs[14].default_value = self.IOR | |
use_diffuse = True | |
# Create the albedo x ao setup. | |
if "albedo" in maps_: | |
if "ao" in maps_: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
aoPath = [item[2] for item in self.textureList if item[1] == "ao"] | |
if len(albedoPath) >= 1 and len(aoPath) >= 1: | |
use_diffuse = False | |
#Add Color>MixRGB node, transform it in the node editor, change it's operation to Multiply and finally we colapse the node. | |
multiplyNode = nodes.new("ShaderNodeMixRGB") | |
multiplyNode.blend_type = 'MULTIPLY' | |
multiplyNode.location = (-250, 320) | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 460) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
#Import ao and ao node setup. | |
aoPath = aoPath[0].replace("\\", "/") | |
aoNode = nodes.new('ShaderNodeTexImage') | |
aoNode.location = (-640, 200) | |
aoNode.image = bpy.data.images.load(aoPath) | |
aoNode.show_texture = True | |
aoNode.image.colorspace_settings.name = colorSpaces[1] | |
# Conned albedo and ao node to the multiply node. | |
mat.node_tree.links.new(multiplyNode.inputs[1], albedoNode.outputs[0]) | |
mat.node_tree.links.new(multiplyNode.inputs[2], aoNode.outputs[0]) | |
# Connect multiply node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], multiplyNode.outputs[0]) | |
else: | |
albedoPath = [item[2] for item in self.textureList if item[1] == "albedo"] | |
if len(albedoPath) >= 1: | |
use_diffuse = False | |
#Import albedo and albedo node setup. | |
albedoPath = albedoPath[0].replace("\\", "/") | |
albedoNode = nodes.new('ShaderNodeTexImage') | |
albedoNode.location = (-640, 420) | |
albedoNode.image = bpy.data.images.load(albedoPath) | |
albedoNode.show_texture = True | |
albedoNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect albedo node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], albedoNode.outputs[0]) | |
# Create the diffuse setup. | |
if "diffuse" in maps_ and use_diffuse: | |
diffusePath = [item[2] for item in self.textureList if item[1] == "diffuse"] | |
if len(diffusePath) >= 1: | |
# Import diffuse and diffuse node setup. | |
diffusePath = diffusePath[0].replace("\\", "/") | |
diffuseNode = nodes.new('ShaderNodeTexImage') | |
diffuseNode.location = (-640, 420) | |
diffuseNode.image = bpy.data.images.load(diffusePath) | |
diffuseNode.show_texture = True | |
diffuseNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect diffuse node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[0], diffuseNode.outputs[0]) | |
use_metalness = True | |
# Create the specular setup. | |
if "specular" in maps_: | |
specularPath = [item[2] for item in self.textureList if item[1] == "specular"] | |
if len(specularPath) >= 1: | |
use_metalness = False | |
# Import specular and specular node setup. | |
specularPath = specularPath[0].replace("\\", "/") | |
specularNode = nodes.new('ShaderNodeTexImage') | |
specularNode.location = (-1150, 200) | |
specularNode.image = bpy.data.images.load(specularPath) | |
specularNode.show_texture = True | |
specularNode.image.colorspace_settings.name = colorSpaces[0] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[5], specularNode.outputs[0]) | |
# Create the metalness setup. Use metalness if specular is missing | |
if "metalness" in maps_ and use_metalness: | |
metalnessPath = [item[2] for item in self.textureList if item[1] == "metalness"] | |
if len(metalnessPath) >= 1: | |
# Import specular and specular node setup. | |
metalnessPath = metalnessPath[0].replace("\\", "/") | |
metalnessNode = nodes.new('ShaderNodeTexImage') | |
metalnessNode.location = (-1150, 200) | |
metalnessNode.image = bpy.data.images.load(metalnessPath) | |
metalnessNode.show_texture = True | |
metalnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect specular node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[4], metalnessNode.outputs[0]) | |
use_gloss = True | |
# Create the roughness setup. | |
if "roughness" in maps_: | |
roughnessPath = [item[2] for item in self.textureList if item[1] == "roughness"] | |
if len(roughnessPath) >= 1: | |
use_gloss = False | |
# Import roughness and roughness node setup. | |
roughnessPath = roughnessPath[0].replace("\\", "/") | |
roughnessNode = nodes.new('ShaderNodeTexImage') | |
roughnessNode.location = (-1150, -60) | |
roughnessNode.image = bpy.data.images.load(roughnessPath) | |
roughnessNode.show_texture = True | |
roughnessNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], roughnessNode.outputs[0]) | |
# Create the gloss setup. | |
if "gloss" in maps_ and use_gloss: | |
glossPath = [item[2] for item in self.textureList if item[1] == "gloss"] | |
if len(glossPath) >= 1: | |
# Add vector>bump node | |
invertNode = nodes.new("ShaderNodeInvert") | |
invertNode.location = (-250, 68) | |
# Import roughness and roughness node setup. | |
glossPath = glossPath[0].replace("\\", "/") | |
glossNode = nodes.new('ShaderNodeTexImage') | |
glossNode.location = (-1150, -60) | |
glossNode.image = bpy.data.images.load(glossPath) | |
glossNode.show_texture = True | |
glossNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add glossNode to invertNode connection | |
mat.node_tree.links.new(invertNode.inputs[1], glossNode.outputs[0]) | |
# Connect roughness node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[7], invertNode.outputs[0]) | |
# Create the opacity setup. | |
if "opacity" in maps_: | |
opacityPath = [item[2] for item in self.textureList if item[1] == "opacity"] | |
if len(opacityPath) >= 1: | |
# Import opacity and opacity node setup. | |
opacityPath = opacityPath[0].replace("\\", "/") | |
opacityNode = nodes.new('ShaderNodeTexImage') | |
opacityNode.location = (-1550, -160) | |
opacityNode.image = bpy.data.images.load(opacityPath) | |
opacityNode.show_texture = True | |
opacityNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect opacity node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[18], opacityNode.outputs[0]) | |
# Create the translucency setup. | |
if "translucency" in maps_: | |
translucencyPath = [item[2] for item in self.textureList if item[1] == "translucency"] | |
if len(translucencyPath) >= 1: | |
# Import translucency and translucency node setup. | |
translucencyPath = translucencyPath[0].replace("\\", "/") | |
translucencyNode = nodes.new('ShaderNodeTexImage') | |
translucencyNode.location = (-1550, -420) | |
translucencyNode.image = bpy.data.images.load(translucencyPath) | |
translucencyNode.show_texture = True | |
translucencyNode.image.colorspace_settings.name = colorSpaces[1] | |
# Connect translucency node to the material parent node. | |
mat.node_tree.links.new(nodes.get(parentName).inputs[15], translucencyNode.outputs[0]) | |
setup_normal = True | |
# Create the normal + bump setup. | |
if "normal" in maps_ and "bump" in maps_: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(normalPath) >= 1 and len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -130) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-640, -400) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-1150, -580) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
mat.node_tree.links.new(bumpNode.inputs[5], normalNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "normal" in maps_ and setup_normal: | |
normalPath = [item[2] for item in self.textureList if item[1] == "normal"] | |
if len(normalPath) >= 1: | |
setup_normal = False | |
# Add vector>normal map node | |
normalNode = nodes.new("ShaderNodeNormalMap") | |
normalNode.location = (-250, -170) | |
# Import normal map and normal map node setup. | |
normalPath = normalPath[0].replace("\\", "/") | |
normalMapNode = nodes.new('ShaderNodeTexImage') | |
normalMapNode.location = (-640, -207) | |
normalMapNode.image = bpy.data.images.load(normalPath) | |
normalMapNode.show_texture = True | |
normalMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add normalMapNode to normalNode connection | |
mat.node_tree.links.new(normalNode.inputs[1], normalMapNode.outputs[0]) | |
# Add normalNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], normalNode.outputs[0]) | |
# Create the normal setup if the LiveLink did not setup normal + bump. | |
if "bump" in maps_ and setup_normal: | |
bumpPath = [item[2] for item in self.textureList if item[1] == "bump"] | |
if len(bumpPath) >= 1: | |
setup_normal = False | |
# Add vector>bump node | |
bumpNode = nodes.new("ShaderNodeBump") | |
bumpNode.location = (-250, -170) | |
bumpNode.inputs[0].default_value = 0.1 | |
# Import bump map and bump map node setup. | |
bumpPath = bumpPath[0].replace("\\", "/") | |
bumpMapNode = nodes.new('ShaderNodeTexImage') | |
bumpMapNode.location = (-640, -207) | |
bumpMapNode.image = bpy.data.images.load(bumpPath) | |
bumpMapNode.show_texture = True | |
bumpMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add bumpMapNode and normalNode connection to the bumpNode | |
mat.node_tree.links.new(bumpNode.inputs[2], bumpMapNode.outputs[0]) | |
# Add bumpNode connection to the material parent node | |
mat.node_tree.links.new(nodes.get(parentName).inputs[19], bumpNode.outputs[0]) | |
# Create the displacement setup. | |
if "displacement" in maps_: | |
if self.DisplacementSetup == "adaptive": | |
displacementPath = [item[2] for item in self.textureList if item[1] == "displacement"] | |
if len(displacementPath) >= 1: | |
# Add vector>displacement map node | |
displacementNode = nodes.new("ShaderNodeDisplacement") | |
displacementNode.location = (10, -400) | |
displacementNode.inputs[0].default_value = 0.1 | |
displacementNode.inputs[1].default_value = 0 | |
# Add converter>RGB Separator node | |
RGBSplitterNode = nodes.new("ShaderNodeSeparateRGB") | |
RGBSplitterNode.location = (-250, -499) | |
# Import normal map and normal map node setup. | |
displacementPath = displacementPath[0].replace("\\", "/") | |
displacementMapNode = nodes.new('ShaderNodeTexImage') | |
displacementMapNode.location = (-640, -740) | |
displacementMapNode.image = bpy.data.images.load(displacementPath) | |
displacementMapNode.show_texture = True | |
displacementMapNode.image.colorspace_settings.name = colorSpaces[1] | |
# Add displacementMapNode to RGBSplitterNode connection | |
mat.node_tree.links.new(RGBSplitterNode.inputs[0], displacementMapNode.outputs[0]) | |
# Add RGBSplitterNode to displacementNode connection | |
mat.node_tree.links.new(displacementNode.inputs[2], RGBSplitterNode.outputs[0]) | |
# Add normalNode connection to the material output displacement node | |
mat.node_tree.links.new(nodes.get(materialOutputName).inputs[2], displacementNode.outputs[0]) | |
mat.cycles.displacement_method = 'BOTH' | |
if self.DisplacementSetup == "regular": | |
pass | |
return | |
class ms_Init(threading.Thread): | |
#Initialize the thread and assign the method (i.e. importer) to be called when it receives JSON data. | |
def __init__(self, importer): | |
threading.Thread.__init__(self) | |
self.importer = importer | |
#Start the thread to start listing to the port. | |
def run(self): | |
try: | |
run_livelink = True | |
host, port = 'localhost', 28888 | |
#Making a socket object. | |
socket_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
#Binding the socket to host and port number mentioned at the start. | |
socket_.bind((host, port)) | |
#Run until the thread starts receiving data. | |
while run_livelink: | |
socket_.listen(5) | |
#Accept connection request. | |
client, addr = socket_.accept() | |
data = "" | |
buffer_size = 4096*2 | |
#Receive data from the client. | |
data = client.recv(buffer_size) | |
if data == b'Bye Megascans': | |
run_livelink = False | |
break | |
#If any data is received over the port. | |
if data != "": | |
self.TotalData = b"" | |
self.TotalData += data #Append the previously received data to the Total Data. | |
#Keep running until the connection is open and we are receiving data. | |
while run_livelink: | |
#Keep receiving data from client. | |
data = client.recv(4096*2) | |
if data == b'Bye Megascans': | |
run_livelink = False | |
break | |
#if we are getting data keep appending it to the Total data. | |
if data : self.TotalData += data | |
else: | |
#Once the data transmission is over call the importer method and send the collected TotalData. | |
self.importer(self.TotalData) | |
break | |
except Exception as e: | |
print( "Megascans LiveLink Error initializing the thread. Error: ", str(e) ) | |
class thread_checker(threading.Thread): | |
#Initialize the thread and assign the method (i.e. importer) to be called when it receives JSON data. | |
def __init__(self): | |
threading.Thread.__init__(self) | |
#Start the thread to start listing to the port. | |
def run(self): | |
try: | |
run_checker = True | |
while run_checker: | |
time.sleep(3) | |
for i in threading.enumerate(): | |
if(i.getName() == "MainThread" and i.is_alive() == False): | |
host, port = 'localhost', 28888 | |
s = socket.socket() | |
s.connect((host,port)) | |
data = "Bye Megascans" | |
s.send(data.encode()) | |
s.close() | |
run_checker = False | |
break | |
except Exception as e: | |
print( "Megascans LiveLink Error initializing thread checker. Error: ", str(e) ) | |
pass | |
class MS_Init_LiveLink(bpy.types.Operator): | |
bl_idname = "ms_livelink.py" | |
bl_label = "Megascans LiveLink" | |
socketCount = 0 | |
def execute(self, context): | |
try: | |
globals()['Megascans_DataSet'] = None | |
self.thread_ = threading.Thread(target = self.socketMonitor) | |
self.thread_.start() | |
bpy.app.timers.register(self.newDataMonitor) | |
return {'FINISHED'} | |
except Exception as e: | |
print( "Megascans LiveLink error starting blender plugin. Error: ", str(e) ) | |
return {"FAILED"} | |
def newDataMonitor(self): | |
try: | |
if globals()['Megascans_DataSet'] != None: | |
MS_Init_ImportProcess() | |
globals()['Megascans_DataSet'] = None | |
except Exception as e: | |
print( "Megascans LiveLink error starting blender plugin (newDataMonitor). Error: ", str(e) ) | |
return {"FAILED"} | |
return 1.0 | |
def socketMonitor(self): | |
try: | |
#Making a thread object | |
threadedServer = ms_Init(self.importer) | |
#Start the newly created thread. | |
threadedServer.start() | |
#Making a thread object | |
thread_checker_ = thread_checker() | |
#Start the newly created thread. | |
thread_checker_.start() | |
except Exception as e: | |
print( "Megascans LiveLink error starting blender plugin (socketMonitor). Error: ", str(e) ) | |
return {"FAILED"} | |
def importer (self, recv_data): | |
try: | |
globals()['Megascans_DataSet'] = recv_data | |
except Exception as e: | |
print( "Megascans LiveLink error starting blender plugin (importer). Error: ", str(e) ) | |
return {"FAILED"} | |
def show_error_dialog(self, context, message = "Test message."): | |
self.report({'INFO'}, message) | |
def menu_func_import(self, context): | |
self.layout.operator(MS_Init_LiveLink.bl_idname, text="Megascans LiveLink") | |
def register(): | |
bpy.utils.register_class(MS_Init_LiveLink) | |
bpy.types.TOPBAR_MT_file_import.append(menu_func_import) | |
def unregister(): | |
# bpy.utils.unregister_class(MS_Init_LiveLink) | |
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) | |
if __name__ == "__main__": | |
register() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment