Axon, ComfyUI-NoobXL
3064 words
15 minutes
quixel导入blender的bl插件以及中文化适配&bug修改
Quixel是来自Epic的一个模型资产库,其中的Bridge可以将资产库下载到本地并且进行个性化导出,如Blender、3dsmax、unity等等。但是遗憾的是blender在2.8版本后就不再支持。
不过Quixel将他们的导入blender插件进行了开源,有人将这个项目进行了fork并做修改。主要是适配了新版blender部分api和节点名字的变化。但是在导入的时候我们会发现导入后的物体大小放大了100倍,而且此时的旋转出现了绕x轴旋转了-90°。(其实旋转是fbx的通病,因为fbx和blender默认的坐标轴的定义不一致出现的错误,主要是着手修改放大问题)
所以我们找到blender的插件地址进行修改。地址在
"C:\Users\Danny\AppData\Roaming\Blender Foundation\Blender\4.2\scripts\addons\NodePreview\__init__.py"
修改一:将Principled BSDF改为”原理化 BSDF“
位于代码的340行。这是节点生成器的一部分,由于blender更改语言会导致节点api名称也发生变化,因此我们将
self.parentName = "Principled BSDF"修改为
self.parentName = "原理化 BSDF"。
修改二:增加导入后的缩放锁定&旋转适配
位于代码的198行。导入fbx后我们需要对缩放进行调整。
if meshFormat.lower() == "fbx": bpy.ops.import_scene.fbx(filepath=meshPath)此时我们要修改成为
if meshFormat.lower() == "fbx": bpy.ops.import_scene.fbx(filepath=meshPath,global_scale=0.1,axis_forward='Z',axis_up='Y') # 缩放和旋转调整 imported_objects = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH'] for obj in imported_objects: obj.scale = (0.01, 0.01, 0.01) # 将模型缩小100倍 obj.rotation_euler[0] += 1.5708 # 绕X轴旋转 90 度 (1.5708弧度 ≈ 90度)
# get selected objects obj_objects = [ o for o in bpy.context.scene.objects if o.select_get() ] self.selectedObjects += obj_objects对bpy.ops.import_scene.fbx(filepath=meshPath,global_scale=0.1,axis_forward=‘Z’,axis_up=‘Y’)之后进行了缩放调整和绕x轴旋转90°的调整。
最后附上修改后的源码:
# ##### QUIXEL AB - MEGASCANS PLugin FOR BLENDER ####### The Megascans Plugin 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 PLUGIN FOR BLENDER #####
import bpy, threading, os, time, json, socketfrom bpy.app.handlers import persistent
globals()['Megascans_DataSet'] = None
# This stuff is for the Alembic supportglobals()['MG_Material'] = []globals()['MG_AlembicPath'] = []globals()['MG_ImportComplete'] = False
bl_info = { "name": "Megascans Plugin Fork", "description": "Connects Blender to Quixel Bridge for one-click imports with shader setup and geometry", "author": "Quixel \ Sören Schmidt-Clausen", "version": (3, 7, 0), "blender": (3, 4, 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():
# 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. def __init__(self): print("Initialized import class...") try: # Check if there's any incoming data if globals()['Megascans_DataSet'] != None:
globals()['MG_AlembicPath'] = [] globals()['MG_Material'] = [] globals()['MG_ImportComplete'] = False
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.activeLOD = self.json_data["activeLOD"] self.minLOD = self.json_data["minLOD"] 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') self.isScatterAsset = self.CheckScatterAsset() self.textureList = [] self.isBillboard = self.CheckIsBillboard() self.ApplyToSelection = False self.isSpecularWorkflow = True self.isAlembic = False
self.NormalSetup = False self.BumpSetup = False
if "workflow" in self.json_data.keys(): self.isSpecularWorkflow = bool(self.json_data["workflow"] == "specular")
if "applyToSelection" in self.json_data.keys(): self.ApplyToSelection = bool(self.json_data["applyToSelection"])
if (self.isCycles): if(bpy.context.scene.cycles.feature_set == 'EXPERIMENTAL'): self.DisplacementSetup = 'adaptive'
texturesListName = "components" if(self.isBillboard): texturesListName = "components"
# Get a list of all available texture maps. item[1] returns the map type (albedo, normal, etc...). self.textureTypes = [obj["type"] for obj in self.json_data[texturesListName]] self.textureList = []
for obj in self.json_data[texturesListName]: texFormat = obj["format"] texType = obj["type"] texPath = obj["path"]
if texType == "displacement" and texFormat != "exr": texDir = os.path.dirname(texPath) texName = os.path.splitext(os.path.basename(texPath))[0]
if os.path.exists(os.path.join(texDir, texName + ".exr")): texPath = os.path.join(texDir, texName + ".exr") texFormat = "exr" # Replace diffuse texture type with albedo so we don't have to add more conditions to handle diffuse map. if texType == "diffuse" and "albedo" not in self.textureTypes: texType = "albedo" self.textureTypes.append("albedo") self.textureTypes.remove("diffuse")
# Normal / Bump setup checks if texType == "normal": self.NormalSetup = True if texType == "bump": self.BumpSetup = True
self.textureList.append((texFormat, texType, texPath))
# 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 self.colorSpaces = ["sRGB", "Non-Color", "Linear"]
# Initialize the import method to start building our shader and import our geometry self.initImportProcess() print("Imported asset from " + self.assetName + " Quixel Bridge")
if len(globals()['MG_AlembicPath']) > 0: globals()['MG_ImportComplete'] = True except Exception as e: print( "Megascans Plugin 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:
if(self.ApplyToSelection and self.assetType not in ["3dplant", "3d"]): self.CollectSelectedObjects()
self.ImportGeometry() self.CreateMaterial() self.ApplyMaterialToGeometry() if(self.isScatterAsset and len(self.selectedObjects) > 1): self.ScatterAssetSetup() elif (self.assetType == "3dplant" and len(self.selectedObjects) > 1): self.PlantAssetSetup()
self.SetupMaterial()
if self.isAlembic: globals()['MG_Material'].append(self.mat)
except Exception as e: print( "Megascans Plugin Error while importing textures/geometry or setting up material. Error: ", str(e) )
def ImportGeometry(self): try: # Import geometry abcPaths = [] 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,global_scale=0.1,axis_forward='Z',axis_up='Y') # 缩放和旋转调整 imported_objects = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH'] for obj in imported_objects: obj.scale = (0.01, 0.01, 0.01) # 将模型缩小100倍 obj.rotation_euler[0] += 1.5708 # 绕X轴旋转 90 度 (1.5708弧度 ≈ 90度)
# 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": if bpy.app.version < (2, 92, 0): bpy.ops.import_scene.obj(filepath=meshPath, use_split_objects = True, use_split_groups = True, global_clight_size = 1.0) else: bpy.ops.import_scene.obj(filepath=meshPath, use_split_objects = True, use_split_groups = True, global_clamp_size = 1.0) # 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": self.isAlembic = True abcPaths.append(meshPath)
if self.isAlembic: globals()['MG_AlembicPath'].append(abcPaths) except Exception as e: print( "Megascans Plugin Error while importing textures/geometry or setting up material. Error: ", str(e) )
def dump(self, obj): for attr in dir(obj): print("obj.%s = %r" % (attr, getattr(obj, attr)))
def CollectSelectedObjects(self): try: sceneSelectedObjects = [ o for o in bpy.context.scene.objects if o.select_get() ] for obj in sceneSelectedObjects: if obj.type == "MESH": self.selectedObjects.append(obj) except Exception as e: print("Megascans Plugin Error::CollectSelectedObjects::", str(e) )
def ApplyMaterialToGeometry(self): for obj in self.selectedObjects: # assign material to obj obj.active_material = self.mat
def CheckScatterAsset(self): if('scatter' in self.json_data['categories'] or 'scatter' in self.json_data['tags'] or 'cmb_asset' in self.json_data['categories'] or 'cmb_asset' in self.json_data['tags']): return True return False
def CheckIsBillboard(self): # Use billboard textures if importing the Billboard LOD. if(self.assetType == "3dplant"): if (self.activeLOD == self.minLOD): return True return False
#Add empty parent for the scatter assets. def ScatterAssetSetup(self): bpy.ops.object.empty_add(type='ARROWS') emptyRefList = [ o for o in bpy.context.scene.objects if o.select_get() and o not in self.selectedObjects ] for scatterParentObject in emptyRefList: scatterParentObject.name = self.assetID + "_" + self.assetName for obj in self.selectedObjects: obj.parent = scatterParentObject break
#Add empty parent for plants. def PlantAssetSetup(self): bpy.ops.object.empty_add(type='ARROWS') emptyRefList = [ o for o in bpy.context.scene.objects if o.select_get() and o not in self.selectedObjects ] for plantParentObject in emptyRefList: plantParentObject.name = self.assetID + "_" + self.assetName for obj in self.selectedObjects: obj.parent = plantParentObject break
# 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 all asset types. Some type specific functionality is also handled here. def SetupMaterial (self): if "albedo" in self.textureTypes: if "ao" in self.textureTypes: self.CreateTextureMultiplyNode("albedo", "ao", -250, 320, -640, 460, -640, 200, 0, 1, True, "Base Color") else: self.CreateTextureNode("albedo", -640, 420, 0, True, "Base Color")
if self.isSpecularWorkflow: if "specular" in self.textureTypes: self.CreateTextureNode("specular", -1150, 200, 0, True, "Specular")
if "gloss" in self.textureTypes: glossNode = self.CreateTextureNode("gloss", -1150, -60) invertNode = self.CreateGenericNode("ShaderNodeInvert", -250, 60) # Add glossNode to invertNode connection self.node_group.links.new(invertNode.inputs["Color"], glossNode.outputs["Color"]) # Connect roughness node to the material parent node. self.ConnectNodeToMaterial("Roughness", invertNode) elif "roughness" in self.textureTypes: self.CreateTextureNode("roughness", -1150, -60, 1, True, "Roughness") else: if "metalness" in self.textureTypes: self.CreateTextureNode("metalness", -1150, 200, 1, True, "Metallic")
if "roughness" in self.textureTypes: self.CreateTextureNode("roughness", -1150, -60, 1, True, "Roughness") elif "gloss" in self.textureTypes: glossNode = self.CreateTextureNode("gloss", -1150, -60) invertNode = self.CreateGenericNode("ShaderNodeInvert", -250, 60) # Add glossNode to invertNode connection self.node_group.links.new(invertNode.inputs["Color"], glossNode.outputs["Color"]) # Connect roughness node to the material parent node. self.node_group.links.new(self.nodes.get(self.parentName).inputs["Roughness"], invertNode.outputs["Color"]) self.ConnectNodeToMaterial("Roughness", invertNode)
if "opacity" in self.textureTypes: self.CreateTextureNode("opacity", -1550, -160, 1, True, "Alpha") self.mat.blend_method = 'HASHED'
if "translucency" in self.textureTypes: self.CreateTextureNode("translucency", -1550, -420, 0, True, "Transmission") elif "transmission" in self.textureTypes: self.CreateTextureNode("transmission", -1550, -420, 1, True, "Transmission")
# If HIGH POLY selected > use normal_bump and no displacement # If LODs selected > use corresponding LODs normal + displacement if self.isHighPoly: self.BumpSetup = False self.CreateNormalNodeSetup(True, "Normal")
if "displacement" in self.textureTypes and not self.isHighPoly: self.CreateDisplacementSetup(True)
def CreateMaterial(self): self.mat = (bpy.data.materials.get( self.materialName ) or bpy.data.materials.new( self.materialName )) self.mat.use_nodes = True self.nodes = self.mat.node_tree.nodes self.parentName = "原理化 BSDF" self.materialOutputName = "Material Output"
self.mat.node_tree.nodes[self.parentName].distribution = 'MULTI_GGX' #self.mat.node_tree.nodes[self.parentName].inputs["Metallic"].default_value = 1 if self.isMetal else 0 # Metallic value #self.mat.node_tree.nodes[self.parentName].inputs["IOR"].default_value = self.IOR
#self.mat.node_tree.nodes[self.parentName].inputs["Specular"].default_value = 0 Macht kein Sinn! Sieht mit Specular besser aus. #self.mat.node_tree.nodes[self.parentName].inputs["Clearcoat"].default_value = 0
#Create Node Group self.node_group = bpy.data.node_groups.new(name=self.assetType + "_" + self.materialName, type="ShaderNodeTree")
#Create Input self.node_group_in = self.node_group.nodes.new("NodeGroupInput") self.node_group_in.location = (-1500, 0) self.node_group.interface.new_socket(name="NodeSocketVector", socket_type="NodeSocketVector")
#Create Output self.node_group_out = self.node_group.nodes.new("NodeGroupOutput") self.node_group_out.location = (500, 0) #Outputs are assigned later
#Instance it in node Tree self.node_group_inst = self.mat.node_tree.nodes.new("ShaderNodeGroup") self.node_group_inst.node_tree = self.node_group self.node_group_inst.location = (-600, 297)
self.mappingNode = None
#Hehe do it anyway #if self.isCycles and self.assetType not in ["3d", "3dplant"]: # Create mapping node. self.mappingNode = self.mat.node_tree.nodes.new("ShaderNodeMapping") self.mappingNode.location = (-1000, 389) self.mappingNode.vector_type = 'TEXTURE' # Create texture coordinate node. texCoordNode = self.mat.node_tree.nodes.new("ShaderNodeTexCoord") texCoordNode.location = (-1200, 389) # Connect texCoordNode to the mappingNode self.mat.node_tree.links.new(self.mappingNode.inputs["Vector"], texCoordNode.outputs["UV"])
#Connect mapping node to Group Input self.mat.node_tree.links.new(self.mappingNode.outputs["Vector"], self.node_group_inst.inputs[0])
def CreateTextureNode(self, textureType, PosX, PosY, colorspace = 1, connectToMaterial = False, materialInputIndex = ""): texturePath = self.GetTexturePath(textureType) textureNode = self.CreateGenericNode('ShaderNodeTexImage', PosX, PosY) textureNode.image = bpy.data.images.load(texturePath) textureNode.show_texture = True textureNode.image.colorspace_settings.name = self.colorSpaces[colorspace] # "sRGB", "Non-Color", "Linear"
if textureType in ["albedo", "specular", "translucency"]: if self.GetTextureFormat(textureType) in "exr": textureNode.image.colorspace_settings.name = self.colorSpaces[2] # "sRGB", "Non-Color", "Linear"
if connectToMaterial: self.ConnectNodeToMaterial(materialInputIndex, textureNode)
#Connect Uvs to Vector unput self.node_group.links.new(textureNode.inputs["Vector"], self.node_group_in.outputs[0])
return textureNode
def CreateTextureMultiplyNode(self, aTextureType, bTextureType, PosX, PosY, aPosX, aPosY, bPosX, bPosY, aColorspace, bColorspace, connectToMaterial, materialInputIndex): #Add Color>MixRGB node, transform it in the node editor, change it's operation to Multiply and finally we colapse the node. multiplyNode = self.CreateGenericNode('ShaderNodeMixRGB', PosX, PosY) multiplyNode.blend_type = 'MULTIPLY' #Setup A and B nodes textureNodeA = self.CreateTextureNode(aTextureType, aPosX, aPosY, aColorspace) textureNodeB = self.CreateTextureNode(bTextureType, bPosX, bPosY, bColorspace) # Conned albedo and ao node to the multiply node. self.node_group.links.new(multiplyNode.inputs["Color1"], textureNodeA.outputs["Color"]) self.node_group.links.new(multiplyNode.inputs["Color2"], textureNodeB.outputs["Color"])
if connectToMaterial: self.ConnectNodeToMaterial(materialInputIndex, multiplyNode)
return multiplyNode
def CreateNormalNodeSetup(self, connectToMaterial, materialInputIndex):
bumpNode = None normalNode = None bumpMapNode = None normalMapNode = None
if self.NormalSetup and self.BumpSetup: bumpMapNode = self.CreateTextureNode("bump", -640, -130) normalMapNode = self.CreateTextureNode("normal", -1150, -580) bumpNode = self.CreateGenericNode("ShaderNodeBump", -250, -170) bumpNode.inputs["Strength"].default_value = 0.1 normalNode = self.CreateGenericNode("ShaderNodeNormalMap", -640, -400) # Add normalMapNode to normalNode connection self.node_group.links.new(normalNode.inputs["Color"], normalMapNode.outputs["Color"]) # Add bumpMapNode and normalNode connection to the bumpNode self.node_group.links.new(bumpNode.inputs["Height"], bumpMapNode.outputs["Color"]) if (2, 81, 0) > bpy.app.version: self.node_group.links.new(bumpNode.inputs["Normal"], normalNode.outputs["Normal"]) else: self.node_group.links.new(bumpNode.inputs["Normal"], normalNode.outputs["Normal"]) # Add bumpNode connection to the material parent node if connectToMaterial: self.ConnectNodeToMaterial(materialInputIndex, bumpNode) elif self.NormalSetup: normalMapNode = self.CreateTextureNode("normal", -640, -207) normalNode = self.CreateGenericNode("ShaderNodeNormalMap", -250, -170) # Add normalMapNode to normalNode connection self.node_group.links.new(normalNode.inputs["Color"], normalMapNode.outputs["Color"]) # Add normalNode connection to the material parent node if connectToMaterial: self.ConnectNodeToMaterial(materialInputIndex, normalNode) elif self.BumpSetup: bumpMapNode = self.CreateTextureNode("bump", -640, -207) bumpNode = self.CreateGenericNode("ShaderNodeBump", -250, -170) bumpNode.inputs["Strength"].default_value = 0.1 # Add bumpMapNode and normalNode connection to the bumpNode self.node_group.links.new(bumpNode.inputs["Height"], bumpMapNode.outputs["Color"]) # Add bumpNode connection to the material parent node if connectToMaterial: self.ConnectNodeToMaterial(materialInputIndex, bumpNode)
def CreateDisplacementSetup(self, connectToMaterial): #Achtung könnte was kaputt machen was ich hier gemnacht hab SOEREN displacementMapNode = self.CreateTextureNode("displacement", -640, -740) self.JustConnectToGroupOutput(displacementMapNode, "NodeSocketFloat", "Height")
def JustConnectToGroupOutput(self, textureNode, socket_type, name): #Create the output for the node_group: output = self.node_group.interface.new_socket(name=name, in_out="OUTPUT", socket_type=socket_type)
#Connect to node group self.node_group.links.new(textureNode.outputs[0], self.node_group_out.inputs[name])
def ConnectNodeToMaterial(self, materialInputIndex, textureNode): #Get the input in the Principled BSDF: #Name for the Material bsdf_in = self.nodes.get(self.parentName).inputs[materialInputIndex]
# Remove Factor from ID, I actually have no idea why its even standing there node_socket_id = bsdf_in.bl_idname.replace("Factor", "")
self.JustConnectToGroupOutput(textureNode, node_socket_id, bsdf_in.name)
#Note this doesnt work for duplicate names, but this shouldn't happen here self.mat.node_tree.links.new(self.node_group_inst.outputs[bsdf_in.name], bsdf_in)
def CreateGenericNode(self, nodeName, PosX, PosY): genericNode = self.node_group.nodes.new(nodeName) genericNode.location = (PosX, PosY) return genericNode
def GetTexturePath(self, textureType): for item in self.textureList: if item[1] == textureType: path = item[2]
path = path.replace("\\\\", "*") path = path.replace("\\", "/") path = path.replace("*", "\\\\")
print(path) return path
def GetTextureFormat(self, textureType): for item in self.textureList: if item[1] == textureType: return item[0].lower()
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 Plugin 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 Plugin Error initializing thread checker. Error: ", str(e) ) pass
class MS_Init_LiveLink(bpy.types.Operator):
bl_idname = "bridge.plugin" bl_label = "Megascans Plugin" 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 Plugin 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 Plugin 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 Plugin 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 Plugin Error starting blender plugin (importer). Error: ", str(e) ) return {"FAILED"}
class MS_Init_Abc(bpy.types.Operator):
bl_idname = "ms_livelink_abc.py" bl_label = "Import ABC"
def execute(self, context):
try: if globals()['MG_ImportComplete']:
assetMeshPaths = globals()['MG_AlembicPath'] assetMaterials = globals()['MG_Material']
if len(assetMeshPaths) > 0 and len(assetMaterials) > 0:
materialIndex = 0 old_materials = [] for meshPaths in assetMeshPaths: for meshPath in meshPaths: bpy.ops.wm.alembic_import(filepath=meshPath, as_background_job=False) for o in bpy.context.scene.objects: if o.select_get(): old_materials.append(o.active_material) o.active_material = assetMaterials[materialIndex]
materialIndex += 1
for mat in old_materials: try: if mat is not None: bpy.data.materials.remove(mat) except: pass
globals()['MG_AlembicPath'] = [] globals()['MG_Material'] = [] globals()['MG_ImportComplete'] = False
return {'FINISHED'} except Exception as e: print( "Megascans Plugin Error starting MS_Init_Abc. Error: ", str(e) ) return {"CANCELLED"}
@persistentdef load_plugin(scene): try: bpy.ops.bridge.plugin() except Exception as e: print( "Bridge Plugin Error::Could not start the plugin. Description: ", str(e) )
def menu_func_import(self, context): self.layout.operator(MS_Init_Abc.bl_idname, text="Megascans: Import Alembic Files")
def register(): if len(bpy.app.handlers.load_post) > 0: # Check if trying to register twice. if "load_plugin" in bpy.app.handlers.load_post[0].__name__.lower() or load_plugin in bpy.app.handlers.load_post: return bpy.utils.register_class(MS_Init_LiveLink) bpy.utils.register_class(MS_Init_Abc) bpy.app.handlers.load_post.append(load_plugin) bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
def unregister(): bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) if len(bpy.app.handlers.load_post) > 0: # Check if trying to register twice. if "load_plugin" in bpy.app.handlers.load_post[0].__name__.lower() or load_plugin in bpy.app.handlers.load_post: bpy.app.handlers.load_post.remove(load_plugin) quixel导入blender的bl插件以及中文化适配&bug修改
https://fuwari.vercel.app/posts/quixel导入blender的bl插件以及中文化适配bug修改/