def new_scene(force=True, do_save=True): """ Creates a new Max scene :param force: bool, True if we want to save the scene without any prompt dialog :param do_save: bool, True if you want to save the current scene before creating new scene """ if do_save and not force: save() ACTION_TABLE_ID = 0 NEW_ACTION_ID = "16" rt.EvalMAXScript('actionMan.executeAction ' + str(ACTION_TABLE_ID) + ' "' + str(NEW_ACTION_ID) + '"') return mxs_function = """fn mf = ( local windowHandle = DialogMonitorOPS.GetWindowHandle() if (windowHandle != 0) then ( UIAccessor.PressButtonByName windowHandle "Do&n't Save" ) return true ) """ rt.execute('mf={}'.format(mxs_function)) rt.DialogMonitorOPS.unRegisterNotification(id=rt.Name('forceNewFile')) rt.DialogMonitorOPS.registerNotification(rt.mf, id=rt.Name('forceNewFile')) rt.DialogMonitorOPS.enabled = True rt.actionMan.executeAction(0, '16') # new file macro action rt.DialogMonitorOPS.unRegisterNotification(id=rt.Name('forceNewFile')) rt.DialogMonitorOPS.enabled = False return True
def maximize_perspective(): '''Setup perspective for the render''' rt.viewport.setLayout(rt.Name('layout_1')) rt.viewport.setType(rt.Name('view_persp_user')) rt.viewport.setTM( rt.matrix3(rt.point3(0.707107, 0.353553, -0.612372), rt.point3(-0.707107, 0.353553, -0.612372), rt.point3(0, 0.866025, 0.5), rt.point3(-0.00967026, -70.3466, -552.481)))
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 zdepthchannel(): '''Access the Z-Depth Channel''' prev_renderer = rt.renderers.current rt.renderers.current = rt.Default_Scanline_Renderer() voxelbox = re.compile("^VoxelBox") for tbd in filter(lambda o: voxelbox.match(o.name), list(rt.objects)): rt.delete(tbd) zdepth_name = rt.Name("zdepth") rbmp = rt.render(outputsize=rt.Point2(32, 32), channels=[zdepth_name], vfb=False) z_d = rt.getChannelAsMask(rbmp, zdepth_name) rt.progressStart("Rendering Voxels...") for y in range(1, rbmp.height): print("y =", y) if not rt.progressupdate(100.0 * y / rbmp.height): break pixel_line = rt.getPixels(rbmp, rt.Point2(0, y - 1), rbmp.width) z_line = rt.getPixels(z_d, rt.Point2(0, y - 1), rbmp.width) for x in range(1, rbmp.width): print("x =", x, z_line[x].value) box = rt.box(width=10, length=10, height=(z_line[x].value / 2)) box.pos = rt.Point3(x * 10, -y * 10, 0) box.wirecolor = pixel_line[x] box.name = rt.uniqueName("VoxelBox") rt.progressEnd() rt.renderers.current = prev_renderer
def create_circle_control(name, init_pos=None, radius=10, color=None, axis='z'): """ Creates a circle control :param name: str :param init_pos: list(float, float, float) or None :param radius: float :param color: list(float, float, float) or rt.Point3 :param axis: str """ pos = init_pos or [0, 0, 0] if rt.classOf(pos) != rt.Point3: pos = rt.Point3(*pos) if color and rt.classOf(color) != rt.color: color = rt.color(*color) if not color: color = rt.yellow rt.setCommandPanelTaskMode(rt.Name('modify')) ctrl_name = rt.uniquename(name) base_circle = rt.circle(name=ctrl_name, radius=radius, steps=6, pos=pos) if str(axis).lower() == 'x': xform_mod = rt.xform() rt.addModifier(base_circle, xform_mod) rt.setProperty(xform_mod.gizmo, 'rotation', rt.eulerAngles(0, 90, 0)) elif str(axis).lower() == 'y': xform_mod = rt.xform() rt.addModifier(base_circle, xform_mod) rt.setProperty(xform_mod.gizmo, 'rotation', rt.eulerAngles(90, 0, 0)) base_circle.wirecolor = color rt.convertTo(base_circle, rt.SplineShape) return base_circle
def demo_animation(): '''Show how to do animation''' rt.resetMaxFile(rt.Name('noPrompt')) sphere = rt.sphere() set_animation_ranges() animate_transform(sphere) playback_animation()
def GetBipedComs(self): biped_com_list = [] bipeds = [] #self.log(u'오브젝트 검색 시작') in_time = timeit.default_timer() classOf = rt.classOf bip_class_str = 'Biped_Object' getNode = rt.biped.getNode name = rt.Name('vertical') addList = biped_com_list.append #print(str(len(rt.objects))) biped_com_list = [ node.controller.rootNode for node in rt.objects if node.name.endswith('Footsteps') ] end_time = timeit.default_timer() #print('GetBipedComs 검사종료 시간 : {}'.format(str(timeit.default_timer() - in_time))) #self.log(u'검사종료 : {}'.format(end_time - in_time)) #self.log(u'씬의 바이패드는 {}개가 있습니다.'.format(len(biped_com_list))) for node in biped_com_list: #self.log(node.name) biped_class = bipedSelect(node) bipeds.append(biped_class) if len(bipeds) > 0: self.m_biped_class = bipeds[0] #self.log(u'기본 바이패드로 {}가 선택되었습니다.'.format(self.m_biped.m_bipName)) #print('GetBipedComs 클래스 실행 완료 시간 : {}'.format(str(timeit.default_timer() - in_time))) return tuple(bipeds)
def setUpClass(self): rt.resetMaxFile(rt.Name("noprompt")) self.rootA = rt.Box() self.boxA = rt.Box() self.boxA.parent = self.rootA self.gizmoA = rt.AsoboBoxGizmo() self.gizmoA.parent = self.rootA self.rootB = rt.Box() self.boxb = rt.Box() self.boxb.parent = self.rootB self.gizmoB = rt.AsoboBoxGizmo() self.gizmoB.parent = self.rootB self.rootLOD0 = rt.Box() self.rootLOD0.name = "test_LOD0" self.rootLOD1 = rt.Box() self.rootLOD1.name = "test_LOD1" self.rootLOD2 = rt.Box() self.rootLOD2.name = "test_LOD2" self.rootLOD3 = rt.Box() self.rootLOD3.name = "test_LOD3" self.exportPath = os.path.join( rt.pathConfig.getCurrentProjectFolder(), "Tools\\3DSMAX\\FlightSimPackage\\src\\samples\\Export\\") multiExporter.run(prompt=False, skip_conversion=True)
def set_bone_weight(skin_node, bone_id, vertex_id, weight): """ Sets bone weights for skin modifier :param skin_node: str :param bone_id: int :param vertex_id: int :param weight: float """ rt.subobjectLevel = 1 skin_node = node_utils.get_pymxs_node(skin_node) if not skin_node: return skin_modifier = skin_node.modifiers[rt.Name('Skin')] if not skin_modifier: return vertex_bitarray = rt.BitArray() vertex_indices = [vertex_id] vertex_bitarray.count = len(vertex_indices) for i, index in enumerate(vertex_indices): vertex_bitarray[i] = index skin_modifier.filter_vertices = True rt.skinOps.SelectBone(skin_modifier, bone_id) rt.skinOps.SelectVertices(skin_modifier, vertex_bitarray) rt.skinOps.setWeight(skin_modifier, weight)
def _uvSnaps(self, assetName): originalSelection = rt.execute("selection as array") validShapes = rt.execute( "for o in selection where superClassOf o == geometryClass collect o" ) if len(validShapes) > 10: msg = "There are %s objects for UV snapshots.\nAre you sure you want to include snapshots to the Asset?" % ( len(validShapes)) state = rt.queryBox(msg, title='Too many objects for UV Snapshot') if state: pass else: return assetDirectory = os.path.join(self.directory, assetName) UVdir = os.path.join(assetDirectory, "UV_snaps") if not os.path.isdir(os.path.normpath(UVdir)): os.makedirs(os.path.normpath(UVdir)) rt.execute("max modify mode") for i in validShapes: objName = i.name UVpath = os.path.join(UVdir, '%s_uv.jpg' % objName) rt.select(i) defUnwrapMod = rt.Unwrap_UVW() rt.addModifier(i, defUnwrapMod) defUnwrapMod.setMapChannel = 1 defUnwrapMod.renderuv_fillmode = 0 defUnwrapMod.renderuv_seamColor = rt.Name("green") defUnwrapMod.renderuv_showframebuffer = False defUnwrapMod.renderuv_force2sided = False defUnwrapMod.renderuv_fillColor = rt.Name("black") defUnwrapMod.renderuv_showoverlap = False defUnwrapMod.renderuv_overlapColor = rt.Name("red") defUnwrapMod.renderuv_edgeColor = rt.Name("white") defUnwrapMod.renderuv_visibleedges = True defUnwrapMod.renderuv_invisibleedges = False defUnwrapMod.renderuv_seamedges = False defUnwrapMod.renderUV(UVpath) rt.deleteModifier(i, defUnwrapMod) rt.select(originalSelection)
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 demo_simple_dialog(): """ Entry point for QDialog demo making use of PySide2 and pymxs """ # reset 3ds Max rt.resetMaxFile(rt.Name('noPrompt')) dialog = PyMaxDialog(GetQMaxMainWindow()) dialog.show()
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 create_box_control(name, init_pos=None, length=10, width=10, height=10, color=None): """ Creates a box control :param name: str :param init_pos: :param length: :param width: :param height: :param color: :return: """ pos = init_pos or [0, 0, 0] if rt.classOf(pos) != rt.Point3: pos = rt.Point3(*pos) if color and rt.classOf(color) != rt.color: color = rt.color(*color) if not color: color = rt.yellow rt.setCommandPanelTaskMode(rt.Name('modify')) base_box = rt.Box( lengthsegs=1, widthsegs=1, heightsegs=1, length=length, width=width, height=height, mapcoords=True, pos=pos, isSelected=True) rt.select(base_box) rt.convertTo(base_box, rt.PolyMeshObject) rt.subobjectLevel = 2 edge_bitarray = rt.BitArray() edge_indices = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] edge_bitarray.count = len(edge_indices) for i, index in enumerate(edge_indices): edge_bitarray[i] = index base_box.EditablePoly.SetSelection(rt.Name('Edge'), edge_bitarray) ctrl_name = rt.uniquename(name) base_box.EditablePoly.createShape(ctrl_name, False, base_box) rt.subobjectLevel = 0 rt.delete(base_box) box_ctrl = rt.getNodeByName(ctrl_name) rt.convertTo(box_ctrl, rt.SplineShape) box_ctrl.wirecolor = color rt.CenterPivot(box_ctrl) transform.reset_xform_and_collapse(box_ctrl, freeze=True) return box_ctrl
def register_all_callbacks(): """Register all the callbacks that we know.""" create_maxscript_callback_function() for name in NOTIFICATIONS: # register a maxscript line to call for specified event name # calls the referenced Python function with an hardcoded argument # the optional named 'id' argument is used as a best practice, # to make it easier to find and remove callbacks later rt.callbacks.addScript(rt.Name(name), "pcb(\"{}\")".format(name), id=rt.Name("my_mxs_handler")) # register a Python function to call for event name # the registered function cannot take any arguments # the optional named 'id' argument is used as a best practice, # to make it easier to find and remove callbacks later rt.callbacks.addScript(rt.Name(name), handle_callback, id=rt.Name("my_python_handler"))
def create_items(): """Create all the items in the sample.""" rt.resetMaxFile(rt.Name('noPrompt')) y_line = create_materials(0.0) + 40.0 y_line = create_modifiers(y_line) + 40.0 y_line = create_helpers(y_line) + 40.0 y_line = create_shapes(y_line) + 40.0 y_line = create_objects(y_line) + 40.0 y_line = create_lights(y_line) + 40.0 y_line = create_cameras(y_line) + 40.0
def setUpClass(self): rt.resetMaxFile(rt.Name("noprompt")) projectPath = rt.pathConfig.getCurrentProjectFolder() self.path = os.path.join( projectPath, "Tools\\3DSMAX\\FlightSimPackage\\src\\samples\\Master_Bear_Adult_FOR_TESTING.max" ) perforce.P4edit(self.path) rt.loadMaxFile(self.path, quiet=True) multiExporter.run(prompt=False, skip_conversion=True)
def main(): """Demonstrate cloning""" rt.resetMaxFile(rt.Name('noPrompt')) obj = rt.sphere(radius=3) create_instance_clones(obj, 10, rt.Point3(5, 0, 0)) rt.MaxOps.CloneNodes(obj, cloneType=INST, offset=rt.Point3(0, 25, 0), expandHierarchy=True)
def draw_line_between_two_points(point_a, point_b): """ Draws a spline curve where point_a is its starting point and point_b its end point :param point_a: list(float, float, float) or rt.Point3 :param point_b: list(float, float, float) or rt.Point3 :return: str, name of the new spline """ if rt.classOf(point_a) != rt.Point3: point_a = rt.Point3(*point_a) if rt.classOf(point_b) != rt.Point3: point_b = rt.Point3(*point_b) spline = rt.SplineShape(pos=point_a) rt.addNewSpline(spline) rt.addKnot(spline, 1, rt.Name('corner'), rt.Name('line'), point_a) rt.addKnot(spline, 1, rt.Name('corner'), rt.Name('line'), point_b) rt.updateShape(spline) return spline
def setUpClass(self): rt.resetMaxFile(rt.Name("noprompt")) tp = rt.Box() tp.name = "topParent" fc = rt.Box() fc.name = "firstChild" fc.parent = tp for i in range(10): fsc = rt.Box() fsc.name = "childNum{0}".format(i) fsc.parent = fc print("setup Hierarchy")
def sample(): """Create all existing materials and showcase them.""" def try_create(mat): """Try to create a given material. If not creatable return None.""" try: return mat() except RuntimeError: return None rt.resetMaxFile(rt.Name('noPrompt')) # maximize the view (select a view with only the one viewport) rt.viewport.setLayout(rt.name("layout_1")) # show the material editor in basic mode rt.MatEditor.mode = rt.Name("basic") rt.MatEditor.open() # create a plane for the floor create_floor() # instantiate all materials that can be instantiated materials = filter(lambda x: x is not None, map(try_create, rt.material.classes)) # showcase all materials showcase_materials(list(materials))
def quickpreview(): '''Create a quick preview''' preview_name = path.join(rt.getDir(rt.Name("preview")), "quickpreview.avi") view_size = rt.getViewSize() anim_bmp = rt.bitmap(view_size.x, view_size.y, filename=preview_name) for t in range(int(rt.animationRange.start), int(rt.animationRange.end)): rt.sliderTime = t dib = rt.gw.getViewportDib() rt.copy(dib, anim_bmp) rt.save(anim_bmp) rt.close(anim_bmp) rt.gc() rt.ramplayer(preview_name, "")
def save_file_dialog(title, start_directory=None, pattern=None): """ Shows save file dialog :param title: str :param start_directory: str :param pattern: str :return: str """ start_directory = start_directory or '' return rt.getSaveFileName(caption=rt.Name(title), filename=start_directory, types=pattern)
def main(): """Create a mesh, color it, and output information about its maps.""" # reset the scene rt.resetMaxFile(rt.Name('noPrompt')) # create a mesh mesh = make_pyramid_mesh() print("Updating the color per vertex channel") rt.setNumCPVVerts(mesh, 2) rt.buildVCFaces(mesh) rt.setVertColor(mesh, 1, rt.Color(255, 0, 0)) rt.setVertColor(mesh, 2, rt.Color(0, 0, 255)) rt.setVCFace(mesh, 1, rt.Point3(1, 1, 2)) rt.setVCFace(mesh, 2, rt.Point3(1, 2, 2)) rt.setVCFace(mesh, 3, rt.Point3(2, 2, 2)) rt.setVCFace(mesh, 4, rt.Point3(1, 1, 1)) rt.setCVertMode(mesh, True) rt.update(mesh) output_channels(mesh)
def load_and_verify(): """Load and verify scene_with_app_chunk.max""" rt.resetMaxFile(rt.Name('noPrompt')) rt.loadMaxFile("scene_with_app_chunk.max") print("scene with AppChunk is loaded.") # Find the "MyTeapot123" node teapot_node = rt.getNodeByName("MyTeapot123") if teapot_node is None: print("Error: Incorrect saved scene.") else: print(rt.getAppData(teapot_node, 678)) obj = teapot_node.baseObject print(rt.getAppData(obj, 1234)) print(rt.getAppData(obj, 2345)) rt.clearAllAppData(obj) print("No 9432 app data {}".format(rt.getAppData(obj, 9432) is None)) print("No 7890 app data {}".format(rt.getAppData(teapot_node, 9432) is None)) print(rt.getAppData(teapot_node.Material, 4567))
def setUpClass(self): rt.resetMaxFile(rt.Name("noprompt")) self.A = rt.Box() self.A.name = "x0_levelofdetailzero" self.B = rt.Box() self.B.name = "x12_testingSoul" self.C = rt.Box() self.C.name = "testingSoul_LOD1" self.D = rt.Box() self.D.name = "testing_LOD23_Soul_LOD234" self.GA = rt.BoxGizmo() self.GA.name = "Collider_A" self.GA.parent = self.D self.GB = rt.BoxGizmo() self.GB.name = "Collider_B" self.GB.parent = self.D self.GC = rt.BoxGizmo() self.GC.name = "Collider_C" self.GC.parent = self.GB self.E = rt.Box() self.E.parent = self.GC self.F = rt.Box() self.F.parent = self.GB self.G = rt.Box() self.G.parent = self.GC self.H = rt.Box() self.H.parent = self.GA print("setup LOD")
def move_node(node_name, amount=None, move_vertices=False, use_local_axis=True): """ Moves given node :param node_name: :param amount: :param move_vertices: :param use_local_axis: :return: """ node_to_move = node_utils.get_pymxs_node(node_name) if not node_to_move: return amount = amount or [0, 0, 0] if rt.classOf(amount) != rt.Point3: amount = rt.Point3(*amount) if move_vertices: xform_mod = rt.xform() rt.addModifier(node_to_move, xform_mod) rt.setProperty(xform_mod.gizmo, 'position', amount) rt.CollapseStack(node_to_move) else: if use_local_axis: coordsys = getattr(rt, '%coordsys_context') local_coordsys = rt.Name('local') prev_coordsys = coordsys(local_coordsys, None) # store current coordsys rt.move(node_to_move, amount) # this is done in local axis coordsys(prev_coordsys, None) # restore previous coordsys else: rt.move(node_to_move, amount)
def create_scene(): """Create and save scene_with_app_chunk.max""" rt.resetMaxFile(rt.Name('noPrompt')) # Create a teapot, a scene node and a material instance, they are all # objects of Animatable node = rt.teapot() teapot = node.baseObject mtl = rt.StandardMaterial() node.Material = mtl node.name = "MyTeapot123" # Now add some user specified strings to these objects rt.setAppdata(teapot, 112, "blah comit") rt.setAppdata(teapot, 1234, "I'm a teapot!") rt.setAppdata(teapot, 2345, u"我是一个茶壶!") rt.setAppdata(node, 5678, "Node of teapot") rt.setAppdata(node, 7890, "This is to be removed") rt.deleteAppdata(node, 7890) rt.setAppdata(mtl, 4567, "Material of teapot") rt.saveMaxFile("scene_with_app_chunk.max") print("scene with AppChunk is saved.")
def create_rectangle_control(name, init_pos=None, length=10.0, width=10.0, corner_radius=0.0, color=None, axis='z'): """ Creates a rectangle control :param name: str :param init_pos: :param length: :param width: :param corner_radius: :param color: :param axis: :return: """ pos = init_pos or [0, 0, 0] if rt.classOf(pos) != rt.Point3: pos = rt.Point3(*pos) if color and rt.classOf(color) != rt.color: color = rt.color(*color) if not color: color = rt.yellow rt.setCommandPanelTaskMode(rt.Name('modify')) ctrl_name = rt.uniquename(name) base_rectangle = rt.rectangle(name=ctrl_name, length=length, width=width, cornerRadius=corner_radius, pos=pos) base_rectangle.wirecolor = color if str(axis).lower() == 'x': xform_mod = rt.xform() rt.addModifier(base_rectangle, xform_mod) rt.setProperty(xform_mod.gizmo, 'rotation', rt.eulerAngles(0, 90, 0)) elif str(axis).lower() == 'y': xform_mod = rt.xform() rt.addModifier(base_rectangle, xform_mod) rt.setProperty(xform_mod.gizmo, 'rotation', rt.eulerAngles(90, 0, 0)) rt.convertTo(base_rectangle, rt.SplineShape) return base_rectangle
def lock_selection(): '''Lock all transforms on the selection''' rt.setTransformLockFlags(rt.selection, rt.Name("all"))