def _importAlembic(self, filePath, importSettings, *args, **kwargs): # Set Alembic Options according to the Max Version: v = rt.maxVersion()[0] maxImp_abc = importSettings["alembicImportMax"] if v > 17000: # Alembic export is not supported before 3ds Max 2016 if rt.pluginManager.loadclass(rt.Alembic_Export): if 18000 <= v < 21000: # between versions 2016 - 2018 rt.AlembicImport.CoordinateSystem = rt.Name( maxImp_abc["CoordinateSystem"]) rt.AlembicImport.CacheTimeRange = rt.Name( maxImp_abc["AnimTimeRange"]) rt.AlembicImport.ShapeName = maxImp_abc["ShapeSuffix"] rt.AlembicImport.ImportToRoot = maxImp_abc["ImportToRoot"] rt.AlembicImport.FitTimeRange = maxImp_abc["FitTimeRange"] rt.AlembicImport.SetStartTime = maxImp_abc["SetStartTime"] rt.AlembicExport.StepFrameTime = maxImp_abc[ "SamplesPerFrame"] elif v >= 21000: # version 2019 and up rt.AlembicImport.CoordinateSystem = rt.Name( maxImp_abc["CoordinateSystem"]) rt.AlembicImport.AnimTimeRange = rt.Name( maxImp_abc["AnimTimeRange"]) rt.AlembicImport.ShapeSuffix = maxImp_abc["ShapeSuffix"] rt.AlembicImport.SamplesPerFrame = maxImp_abc[ "SamplesPerFrame"] rt.AlembicImport.Hidden = maxImp_abc["Hidden"] rt.AlembicImport.UVs = maxImp_abc["UVs"] rt.AlembicImport.Normals = maxImp_abc["Normals"] rt.AlembicImport.VertexColors = maxImp_abc["VertexColors"] rt.AlembicImport.ExtraChannels = maxImp_abc[ "ExtraChannels"] rt.AlembicImport.Velocity = maxImp_abc["Velocity"] rt.AlembicImport.MaterialIDs = maxImp_abc["MaterialIDs"] rt.AlembicImport.Visibility = maxImp_abc["Visibility"] rt.AlembicImport.LayerName = maxImp_abc["LayerName"] rt.AlembicImport.MaterialName = maxImp_abc["MaterialName"] rt.AlembicImport.ObjectID = maxImp_abc["ObjectID"] rt.AlembicImport.CustomAttributes = maxImp_abc[ "CustomAttributes"] # Export rt.importFile(filePath, rt.Name("NoPrompt"), using=rt.Alembic_Import) return True else: rt.messageBox("Alembic Plugin cannot be initialized. Skipping", title="Alembic not supported") return False else: rt.messageBox( "There is no alembic support for this version. Skipping", title="Alembic not supported") return False
def _exportRedshift(self, filePath, exportSettings, exportSelected=True, timeRange=[0, 10]): # TODO: // export redshift core function for 3ds Max rt.messageBox("Redshift Proxy Export for 3ds Max is under development", title='Info') return False
def combine_selected_objects(): """ Convert the selected objects into an editable mesh. The selections needs to contain at least 2 objects to combine. """ if len(rt.selection) < 2: msg = "Please select at least 2 nodes to combine." print(msg) rt.messageBox(msg) else: # combine all the selected nodes into one editable mesh combine_objects(*rt.selection)
def _importFbx(self, filePath, importSettings, *args, **kwargs): if rt.pluginManager.loadclass(rt.FBXIMP): maxImp_fbx = importSettings["fbxImportMax"] # Set FBX Options for item in maxImp_fbx.items(): rt.FBXImporterSetParam(rt.Name(item[0]), item[1]) rt.FBXImporterSetParam(rt.Name("UpAxis"), "Z") try: rt.importFile(filePath, rt.Name("NoPrompt"), using=rt.FBXIMP) return True except: msg = "Cannot import FBX file for unknown reason. Skipping import" rt.messageBox(msg, title='Info') return False else: msg = "FBX Plugin cannot be initialized. Skipping import" rt.messageBox(msg, title='Info') return False
def _exportFbx(self, filePath, exportSettings, exportSelected=True, timeRange=[0, 10]): """ Exports FBX (.fbx) file Args: filePath: (String) Absolute File path for exported file exportSettings: (Dictionary) settings file (see getExportSettings) exportSelected: (Boolean) If True, exports only currently selected objects, else whole scene. Default True Returns: (Boolean) True for success False for failure """ if rt.pluginManager.loadclass(rt.FBXEXP): maxExp_fbx = exportSettings["fbxExportMax"] # OVERRIDE ANIMATION SETTINGS if timeRange[0] != timeRange[1]: maxExp_fbx["Animation"] = True maxExp_fbx["BakeFrameStart"] = timeRange[0] maxExp_fbx["BakeFrameEnd"] = timeRange[1] else: maxExp_fbx["Animation"] = False # Set FBX Options for item in maxExp_fbx.items(): rt.FBXExporterSetParam(rt.Name(item[0]), item[1]) try: rt.exportFile(filePath, rt.Name("NoPrompt"), selectedOnly=exportSelected, using=rt.FBXEXP) return True except: msg = "Cannot export FBX for unknown reason. Skipping FBX export" rt.messageBox(msg, title='Info') return False else: msg = "FBX Plugin cannot be initialized. Skipping FBX export" rt.messageBox(msg, title='Info') return False
def applyLayerSelectionToPreset(self, preset, layerNames): r = qtUtils.popup_Yes_No(title="Apply Unsaved Changes ?",text="You need to save the scene to apply") baseFile = rt.maxFilePath + rt.maxFileName if r: preset.edit(layerNames=layerNames) self.btnApplyPresetLayer.setText("Apply") self.treeLayer.isEdited = False if baseFile != None: s = rt.saveMaxFile(baseFile) if s == False: rt.messageBox("Save failed, choose another path, or checkout your file.") f = rt.getSaveFileName(caption="Save as", filename=baseFile) while f == None: f = rt.getSaveFileName(caption="Save as", filename=baseFile) rt.saveMaxFile(f) if f != None else rt.messageBox("Save failed") else: print("successfully saved the scene ") else: while f == None: f = rt.getSaveFileName(caption="Save as", filename=baseFile) rt.saveMaxFile(f) if f != None else rt.messageBox("Save failed")
def _info(self, msg, *args, **kwargs): rt.messageBox(msg, title='Info')
def _exception(self, code, msg, *args, **kwargs): """Overriden Function""" rt.messageBox(msg, title=self.errorCodeDict[code]) if (200 >= code < 210): raise Exception(code, msg)
def saveAsset(self, assetName, exportUV=True, exportOBJ=True, exportFBX=True, exportABC=True, selectionOnly=True, sceneFormat="max", notes="N/A", **info): """ Saves the selected object(s) as an asset into the predefined library """ # self.ssResolution = 1000 if assetName == "": msg = "Asset Name cannot be empty" state = rt.messageBox(msg, title='Info') return if assetName in self.assetsList: msg = "This Asset already exists.\nDo you want to overwrite?" state = rt.queryBox(msg, title='Manager Question') if state: pass else: return originalSelection = self._getSelection(asMaxArray=True) originalPath = self._getSceneFile() dump, origExt = os.path.splitext(originalPath) assetDirectory = os.path.join(self.directory, assetName) assetAbsPath = os.path.join(assetDirectory, "%s%s" % (assetName, u'.%s' % sceneFormat)) if selectionOnly: selection = self._getSelection(asMaxArray=True) if len(selection) == 0: msg = "No object selected" rt.messageBox(msg, title='Info') return else: rt.select(rt.objects) selection = self._getSelection(asMaxArray=True) # originalSelection = self._getSelection(asMaxArray=True) if not os.path.exists(assetDirectory): os.mkdir(assetDirectory) # GET TEXTURES # ------------ if selectionOnly: possibleFileHolders = rt.execute("selection as Array") filteredBitmaps = self._getFileNodes(possibleFileHolders) else: allTexture = rt.usedMaps() allBitmap = rt.getClassInstances(rt.bitmapTexture) # this makes sure only the USED bitmaps will stored filteredBitmaps = [ x for x in allBitmap if x.filename in allTexture ] textureDatabase = [ x for x in self._buildPathDatabase(filteredBitmaps, assetDirectory) ] self._copyTextures(textureDatabase) # CREATE PREVIEWS # --------------- thumbPath, ssPath, swPath = self._createThumbnail( assetName, selectionOnly=selectionOnly, viewFit=True) # CREATE UV SNAPSHOTS # ---------------- rt.select(selection) if exportUV: self._uvSnaps(assetName) # SAVE SOURCE # ----------- fManager.SaveSelected(assetAbsPath) # EXPORT OBJ # ---------- if exportOBJ: objFilePath = os.path.join(assetDirectory, "%s.obj" % assetName) if self._exportObj(objFilePath, exportSettings=self.exportSettings): objName = "{0}.obj".format(assetName) else: objName = "N/A" else: objName = "N/A" # EXPORT FBX # ---------- if exportFBX: fbxFilePath = os.path.join(assetDirectory, "%s.fbx" % assetName) frame = self._getCurrentFrame() if self._exportFbx(fbxFilePath, exportSettings=self.exportSettings, timeRange=[frame, frame]): fbxName = "{0}.fbx".format(assetName) else: fbxName = "N/A" else: fbxName = "N/A" # EXPORT ALEMBIC # -------------- if exportABC: abcFilePath = os.path.join(assetDirectory, "%s.abc" % assetName) frame = self._getCurrentFrame() if self._exportAlembic(abcFilePath, exportSettings=self.exportSettings, timeRange=[frame, frame]): abcName = "{0}.abc".format(assetName) else: abcName = "N/A" else: abcName = "N/A" # NUMERIC DATA # ------------ polyCount = sum(rt.getPolygonCount(x)[0] for x in selection) # polyCount = sum(rt.getPolygonCount(x)[0] for x in countLoop) tiangleCount = sum(rt.getPolygonCount(x)[1] for x in selection) # tiangleCount = sum(rt.getPolygonCount(x)[1] for x in countLoop) versionInfo = rt.maxversion() vInfo = [versionInfo[0], versionInfo[1], versionInfo[2]] # DATABASE # -------- dataDict = {} dataDict['sourceProject'] = "3dsMax" dataDict['version'] = vInfo dataDict['assetName'] = assetName dataDict['objPath'] = objName dataDict['fbxPath'] = fbxName dataDict['abcPath'] = abcName dataDict['sourcePath'] = os.path.basename(assetAbsPath) dataDict['thumbPath'] = os.path.basename(thumbPath) dataDict['ssPath'] = os.path.basename(ssPath) dataDict['swPath'] = os.path.basename(swPath) dataDict['textureFiles'] = [x["Texture"] for x in textureDatabase] dataDict['Faces/Triangles'] = ("%s/%s" % (str(polyCount), str(tiangleCount))) dataDict['origin'] = originalPath dataDict['notes'] = notes self._setData(assetName, dataDict) rt.clearSelection() self._returnOriginal(textureDatabase) # self.scanAssets() # scanning issued at populate function on ui class rt.select(originalSelection) rt.messageBox("Asset Created Successfully", title='Info', beep=False)
def _importObj(self, filePath, importSettings, *args, **kwargs): if rt.pluginManager.loadclass(rt.ObjExp): # Set OBJ Options maxImp_obj = importSettings["objImportMax"] iniPath_importSettings = rt.objImp.getIniName() rt.setINISetting(iniPath_importSettings, "General", "UseLogging", maxImp_obj["UseLogging"]) rt.setINISetting(iniPath_importSettings, "General", "ResetScene", maxImp_obj["ResetScene"]) rt.setINISetting(iniPath_importSettings, "General", "CurrObjColor", maxImp_obj["CurrObjColor"]) rt.setINISetting(iniPath_importSettings, "General", "MapSearchPath", maxImp_obj["MapSearchPath"]) rt.setINISetting(iniPath_importSettings, "Objects", "SingleMesh", maxImp_obj["SingleMesh"]) rt.setINISetting(iniPath_importSettings, "Objects", "AsEditablePoly", maxImp_obj["AsEditablePoly"]) rt.setINISetting(iniPath_importSettings, "Objects", "Retriangulate", maxImp_obj["Retriangulate"]) rt.setINISetting(iniPath_importSettings, "Geometry", "FlipZyAxis", maxImp_obj["FlipZyAxis"]) rt.setINISetting(iniPath_importSettings, "Geometry", "CenterPivots", maxImp_obj["CenterPivots"]) rt.setINISetting(iniPath_importSettings, "Geometry", "Shapes", maxImp_obj["Shapes"]) rt.setINISetting(iniPath_importSettings, "Geometry", "TextureCoords", maxImp_obj["TextureCoords"]) rt.setINISetting(iniPath_importSettings, "Geometry", "SmoothingGroups", maxImp_obj["SmoothingGroups"]) rt.setINISetting(iniPath_importSettings, "Geometry", "NormalsType", maxImp_obj["NormalsType"]) rt.setINISetting(iniPath_importSettings, "Geometry", "SmoothAngle", maxImp_obj["SmoothAngle"]) rt.setINISetting(iniPath_importSettings, "Geometry", "FlipNormals", maxImp_obj["FlipNormals"]) rt.setINISetting(iniPath_importSettings, "Units/Scale", "Convert", maxImp_obj["Convert"]) rt.setINISetting(iniPath_importSettings, "Units/Scale", "ConvertFrom", maxImp_obj["ConvertFrom"]) rt.setINISetting(iniPath_importSettings, "Units/Scale", "ObjScale", maxImp_obj["ObjScale"]) rt.setINISetting(iniPath_importSettings, "Material", "UniqueWireColor", maxImp_obj["UniqueWireColor"]) rt.setINISetting(iniPath_importSettings, "Material", "ImportMaterials", maxImp_obj["ImportMaterials"]) rt.setINISetting(iniPath_importSettings, "Material", "UseMatPrefix", maxImp_obj["UseMatPrefix"]) rt.setINISetting(iniPath_importSettings, "Material", "DefaultBump", maxImp_obj["DefaultBump"]) rt.setINISetting(iniPath_importSettings, "Material", "ForceBlackAmbient", maxImp_obj["ForceBlackAmbient"]) rt.setINISetting(iniPath_importSettings, "Material", "ImportIntoMatEditor", maxImp_obj["ImportIntoMatEditor"]) rt.setINISetting(iniPath_importSettings, "Material", "ShowMapsInViewport", maxImp_obj["ShowMapsInViewport"]) rt.setINISetting(iniPath_importSettings, "Material", "CopyMapsToProj", maxImp_obj["CopyMapsToProj"]) rt.setINISetting(iniPath_importSettings, "Material", "OverwriteImages", maxImp_obj["OverwriteImages"]) rt.importFile(filePath, rt.Name("NoPrompt"), using=rt.ObjImp) return True else: msg = "OBJ Plugin cannot be initialized. Skipping import" rt.messageBox(msg, title='Info') return False
def _exportAlembic(self, filePath, exportSettings, exportSelected=True, timeRange=[0, 10]): """ Exports Alembic (.abc) file Args: filePath: (String) Absolute File path for exported file exportSettings: (Dictionary) settings file (see getExportSettings) exportSelected: (Boolean) If True, exports only currently selected objects, else whole scene. Default True Returns: (Boolean) True for success False for failure """ # Set Alembic Options according to the Max Version: v = rt.maxVersion()[0] maxExp_abc = exportSettings["alembicExportMax"] # override animation related settings maxExp_abc["AnimTimeRange"] = "StartEnd" maxExp_abc["StartFrame"] = timeRange[0] maxExp_abc["EndFrame"] = timeRange[1] if v > 17000: # Alembic export is not supported before 3ds Max 2016 if rt.pluginManager.loadclass(rt.Alembic_Export): if 18000 <= v < 21000: # between versions 2016 - 2018 rt.AlembicExport.CoordinateSystem = rt.Name( maxExp_abc["CoordinateSystem"]) rt.AlembicExport.ArchiveType = rt.Name( maxExp_abc["ArchiveType"]) rt.AlembicExport.ParticleAsMesh = maxExp_abc[ "ParticleAsMesh"] rt.AlembicExport.CacheTimeRange = rt.Name( maxExp_abc["AnimTimeRange"]) rt.AlembicExport.ShapeName = maxExp_abc["ShapeSuffix"] rt.AlembicExport.StepFrameTime = maxExp_abc[ "SamplesPerFrame"] rt.AlembicExport.AnimTimeRange = maxExp_abc[ "AnimTimeRange"] rt.AlembicExport.StartFrame = maxExp_abc["StartFrame"] rt.AlembicExport.EndFrame = maxExp_abc["EndFrame"] elif v >= 21000: # version 2019 and up rt.AlembicExport.CoordinateSystem = rt.Name( maxExp_abc["CoordinateSystem"]) rt.AlembicExport.ArchiveType = rt.Name( maxExp_abc["ArchiveType"]) rt.AlembicExport.ParticleAsMesh = maxExp_abc[ "ParticleAsMesh"] rt.AlembicExport.ShapeSuffix = maxExp_abc["ShapeSuffix"] rt.AlembicExport.SamplesPerFrame = maxExp_abc[ "SamplesPerFrame"] rt.AlembicExport.Hidden = maxExp_abc["Hidden"] rt.AlembicExport.UVs = maxExp_abc["UVs"] rt.AlembicExport.Normals = maxExp_abc["Normals"] rt.AlembicExport.VertexColors = maxExp_abc["VertexColors"] rt.AlembicExport.ExtraChannels = maxExp_abc[ "ExtraChannels"] rt.AlembicExport.Velocity = maxExp_abc["Velocity"] rt.AlembicExport.MaterialIDs = maxExp_abc["MaterialIDs"] rt.AlembicExport.Visibility = maxExp_abc["Visibility"] rt.AlembicExport.LayerName = maxExp_abc["LayerName"] rt.AlembicExport.MaterialName = maxExp_abc["MaterialName"] rt.AlembicExport.ObjectID = maxExp_abc["ObjectID"] rt.AlembicExport.CustomAttributes = maxExp_abc[ "CustomAttributes"] rt.AlembicExport.AnimTimeRange = maxExp_abc[ "AnimTimeRange"] rt.AlembicExport.StartFrame = maxExp_abc["StartFrame"] rt.AlembicExport.EndFrame = maxExp_abc["EndFrame"] # Export rt.exportFile(filePath, rt.Name("NoPrompt"), selectedOnly=exportSelected, using=rt.Alembic_Export) return True else: rt.messageBox("Alembic Plugin cannot be initialized. Skipping", title="Alembic not supported") return False else: rt.messageBox( "There is no alembic support for this version. Skipping", title="Alembic not supported") return False
def _exportObj(self, filePath, exportSettings, exportSelected=True): """ Exports wavefront Obj file Args: filePath: (String) Absolute File path for exported file exportSettings: (Dictionary) settings file (see getExportSettings) exportSelected: (Boolean) If True, exports only currently selected objects, else whole scene. Default True Returns: (Boolean) True for success False for failure """ if rt.pluginManager.loadclass(rt.ObjExp): maxExp_obj = exportSettings["objExportMax"] # Set OBJ Options iniPath_exportSettings = rt.objExp.getIniName() rt.setINISetting(iniPath_exportSettings, "Geometry", "FlipZyAxis", maxExp_obj["FlipZyAxis"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "Shapes", maxExp_obj["Shapes"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "ExportHiddenObjects", maxExp_obj["ExportHiddenObjects"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "FaceType", maxExp_obj["FaceType"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "TextureCoords", maxExp_obj["TextureCoords"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "Normals", maxExp_obj["Normals"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "SmoothingGroups", maxExp_obj["SmoothingGroups"]) rt.setINISetting(iniPath_exportSettings, "Geometry", "ObjScale", maxExp_obj["ObjScale"]) rt.setINISetting(iniPath_exportSettings, "Output", "RelativeIndex", maxExp_obj["RelativeIndex"]) rt.setINISetting(iniPath_exportSettings, "Output", "Target", maxExp_obj["Target"]) rt.setINISetting(iniPath_exportSettings, "Output", "Precision", maxExp_obj["Precision"]) rt.setINISetting(iniPath_exportSettings, "Optimize", "optVertex", maxExp_obj["optVertex"]) rt.setINISetting(iniPath_exportSettings, "Optimize", "optNormals", maxExp_obj["optNormals"]) rt.setINISetting(iniPath_exportSettings, "Optimize", "optTextureCoords", maxExp_obj["optTextureCoords"]) try: rt.exportFile(filePath, rt.Name("NoPrompt"), selectedOnly=exportSelected, using=rt.ObjExp) return True except: msg = "Cannot export OBJ for unknown reason. Skipping OBJ export" rt.messageBox(msg, title='Info') return False # objName = "{0}.obj".format(assetName) else: msg = "Wavefront(Obj) Export Plugin cannot be initialized. Skipping Obj export" rt.messageBox(msg, title='Info') return False
def _importRedshift(self, filePath, importSettings, *args, **kwargs): # TODO: // import redshift core function for 3ds Max rt.messageBox("Redshift Proxy Import for 3ds Max is under development", title='Info')
def _importVray(self, filePath, importSettings, *args, **kwargs): # TODO: // import vray core function for 3ds Max rt.messageBox("Vray Proxy Import for 3ds Max is under development", title='Info')
def runMatSceneCleaner(): global GLOBAL_IDENTICALS_MATS GLOBAL_IDENTICALS_MATS = [] SceneMats = [] UselessMats = [] if MAXVERSION() < MAX2021: AllMatswProps = collections.OrderedDict([]) else: AllMatswProps = {} originalsMats = [] for matClass in rt.material.classes: for m in (rt.getclassinstances(matClass, processAllAnimatables=False, processChildren = True)): SceneMats.append(m) for mi in (rt.getclassinstances(matClass, processAllAnimatables=True, processChildren = True)): if mi not in SceneMats: UselessMats.append(mi) for i in range(len(SceneMats)): #if rt.classOf(SceneMats[i]) == rt.FlightSim: #if rt.classOf(SceneMats[i]) == rt.Physical_Material: //Uncomment if you wants to target specifics material types if MAXVERSION() < MAX2021: MatwProps = collections.OrderedDict([]) else: MatwProps = {} propName = rt.getPropNames(SceneMats[i]) for p in propName: param = rt.getProperty(SceneMats[i], p) MatwProps[p] = param AllMatswProps[SceneMats[i]] = MatwProps for mp in AllMatswProps: PropDicInallMapPropDic(mp, AllMatswProps[mp], AllMatswProps) for dbs in GLOBAL_IDENTICALS_MATS: MeshfromCopies = [] DoublesMatswMultimatnID = [] for dm in dbs.DoublesMats: print("[{0}] was a copy of [{1}], it has been removed and reassigned to [{1}]".format(dm,dbs.OrigMat)) multMatsLinToCurDoublewID = material.getConnectedMultiMaterials(dm, True) if len(multMatsLinToCurDoublewID) != 0: DoublesMatswMultimatnID.append(multMatsLinToCurDoublewID) ndsfromMat = node.get_nodes_by_material(dm) for nfm in ndsfromMat: MeshfromCopies.append(nfm) for DMwM in DoublesMatswMultimatnID: for KeyMulToID in DMwM: material.assignMatToMutliMatFromID(KeyMulToID, dbs.OrigMat, DMwM[KeyMulToID]) for mfc in MeshfromCopies: mfc.material = dbs.OrigMat rt.holdMaxFile() rt.fetchMaxFile(quiet = True) copyCounter = 0 for dbs1 in GLOBAL_IDENTICALS_MATS: for dm1 in dbs.DoublesMats: copyCounter +=1 rt.messageBox("Double Mat Scene Cleaner Tool finished. It has found and reasigned {0} materials duplicates".format(copyCounter))