def quick_pivot_align(source_node, target_node): """ Aligns pivot in position and orientation :param source_node: str :param target_node: str """ source_node = node_utils.get_pymxs_node(source_node) target_node = node_utils.get_pymxs_node(target_node) source_node.pivot = target_node.pivot
def parent_node(max_child_node, max_parent_node): """ Parents child node into parent node hierarchy :param max_child_node: MaxPlus.INode :param max_parent_node: MaxPlus.INode """ max_child_node = node.get_pymxs_node(max_child_node) max_parent_node = node.get_pymxs_node(max_parent_node) max_child_node.parent = max_parent_node return max_child_node
def quick_align(source_node, target_node, freeze=False): """ Aligns to objects in position :param source_node: str :param target_node: str :param freeze: bool """ source_node = node_utils.get_pymxs_node(source_node) target_node = node_utils.get_pymxs_node(target_node) source_node.position = target_node.position if freeze: freeze_transform(source_node)
def match_transforms(source_node, target_node, freeze=False): """ Matches the transform of the source node to the transform of the target node :param source_node: str :param target_node: str :param freeze: bool """ source_node = node_utils.get_pymxs_node(source_node) target_node = node_utils.get_pymxs_node(target_node) orig_xform = target_node.transform source_node.transform = orig_xform if freeze: freeze_transform(source_node)
def is_attribute_locked(node, attribute_name): """ Returns whether given attribute is locked or not :param node: str :param attribute_name: str :return: bool """ node = node_utils.get_pymxs_node(node) lock_flags = list(rt.getTransformLockFlags(node)) xform_attrs = [ max_constants.TRANSLATION_ATTR_NAME, max_constants.ROTATION_ATTR_NAME, max_constants.SCALE_ATTR_NAME ] for name, flags_list in zip(xform_attrs, [[0, 1, 2], [3, 4, 5], [6, 7, 8]]): if name in attribute_name: if attribute_name == name: for flag_index in flags_list: if not lock_flags[flag_index]: return False return True else: for i, (axis, flag_value) in enumerate(zip('XYZ', flags_list)): flag_index = flags_list[i] if attribute_name == '{}{}'.format( name, axis) and lock_flags[flag_index]: return True return False
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 freeze_transform(node_name): """ Freezes the transform for the given node :param node_name: str """ node = node_utils.get_pymxs_node(node_name) rotation_controller = rt.getPropertyController(node.controller, 'Rotation') position_controller = rt.getPropertyController(node.controller, 'Position') if rt.classOf(rotation_controller) != rt.Rotation_Layer: rotation_list_controller = rt.Rotation_List() rt.setPropertyController(node.controller, 'Rotation', rotation_list_controller) rt.setPropertyController(rotation_list_controller, 'Available', rt.Euler_Xyz()) rotation_list_controller.setName(1, 'Frozen Rotation') rotation_list_controller.setName(2, 'Zero Euler XYZ') rotation_list_controller.setActive(2) if rt.classOf(position_controller) != rt.Position_Layer: position_list_controller = rt.Position_List() rt.setPropertyController(node.controller, 'Position', position_list_controller) pos_xyz_controller = rt.Position_XYZ() rt.setPropertyController(position_list_controller, 'Available', pos_xyz_controller) position_list_controller.setName(1, 'Frozen Position') position_list_controller.setName(2, 'Zero Pos XYZ') position_list_controller.setActive(2) pos_xyz_controller.x_Position = 0 pos_xyz_controller.y_Position = 0 pos_xyz_controller.z_Position = 0
def node_exists(node): """ Returns whether given object exists or not :return: bool """ node = node_utils.get_pymxs_node(node) return rt.isValidNode(node)
def match_position(source_node, target_node, freeze=False): """ Matches the position of the source node to the position of the target node :param source_node: str :param target_node: str :param freeze: bool :return: """ source_node = node_utils.get_pymxs_node(source_node) target_node = node_utils.get_pymxs_node(target_node) orig_xform = source_node.transform orig_xform.pos = target_node.transform.translationPart source_node.transform = orig_xform if freeze: freeze_transform(source_node)
def deselect_node(node): """ Deselects given node from current selection :param node: str """ node = node_utils.get_pymxs_node(node) return rt.deselect(node)
def link_object(source_node, parent_node, freeze=True, hierarchy=False): """ Links an object and freezes it transforms (optional) :param source_node: str :param parent_node: str :param freeze: bool :param hierarchy: bool """ source_node = node_utils.get_pymxs_node(source_node) parent_node = node_utils.get_pymxs_node(parent_node) source_node.parent = parent_node if freeze: freeze_transform(source_node) if hierarchy: # TODO: Freeze character pass
def _get_scope_list(self): """ Internal function used to get the scope list for the increment string :return: list<str> """ if node_utils.get_pymxs_node(self.increment_string): return [self.increment_string] return list()
def add_shape(curve_spline1, curve_spline2): """ Combines given two curve splines :param curve_spline1: :param curve_spline2: :return: """ curve_spline1 = node_utils.get_pymxs_node(curve_spline1) curve_spline2 = node_utils.get_pymxs_node(curve_spline2) if not curve_spline1 or not curve_spline2: return if not rt.isKindOf(curve_spline1, rt.SplineShape) or not rt.isKindOf(curve_spline2, rt.SplineShape): return rt.convertToSplineShape(curve_spline1) rt.convertToSplineShape(curve_spline2) rt.addAndWeld(curve_spline1, curve_spline2, -1) return curve_spline1
def reset_pivot_to_origin(node_name, align_to_world=False): """ Resets the pivot of the given node to the origin of the world :param node_name: :param align_to_world: """ node = node_utils.get_pymxs_node(node_name) node.pivot = rt.Point3(0, 0, 0) if align_to_world: rt.WorldAlignPivot(node)
def unparent_node(max_node): """ Unparents given Max node :param max_node: MaxPlus.INode """ max_node = node.get_pymxs_node(max_node) max_node.parent = None return max_node
def reset_xform_and_collapse(node_name, freeze=False): """ Resets the xform and collapse the stack of the given node :param node_name: str :param freeze: bool """ node = node_utils.get_pymxs_node(node_name) rt.ResetXForm(node) rt.CollapseStack(node) if freeze: freeze_transform(node)
def rename_node(node, new_name, **kwargs): """ Renames given node with new given name :param node: str :param new_name: str :return: str """ pymxs_node = node_utils.get_pymxs_node(node) pymxs_node.name = new_name return pymxs_node.name
def select_node(node, replace_selection=True, **kwargs): """ Selects given object in the current scene :param replace_selection: bool :param node: str """ node = node_utils.get_pymxs_node(node) if replace_selection: return rt.select(node) else: return rt.selectMore(node)
def _find_unique_name(obj_name): node = node_utils.get_pymxs_node(obj_name) if not node: return obj_name unique = FindUniqueName(obj_name) unique.get_last_number(include_last_number) unique_name = unique.get() unique_name = rt.uniquename(unique_name) if do_rename: node.name = unique_name return node.name else: return unique_name
def get_attribute_value(node, attribute_name): """ Returns the value of the given attribute in the given node :param node: str :param attribute_name: str :return: variant """ node = node_utils.get_pymxs_node(node) try: return rt.getProperty(node, attribute_name) except Exception: return None
def select_nodes(max_nodes, add=False): """ Select given Max nodes :param max_nodes: list<MaxPlus.INode> :param add: bool, Whether to add given node names to an existing selection or not """ max_nodes = python.force_list(max_nodes) sel = list() if not add else list(rt.selection) for max_node in max_nodes: max_node = node.get_pymxs_node(max_node) sel.append(max_node) if not sel: return rt.select(sel)
def set_float_attribute_value(node, attribute_name, attribute_value, clamp=False): """ Sets the integer value of the given attribute in the given node :param node: str :param attribute_name: str :param attribute_value: int :param clamp: bool :return: """ node = node_utils.get_pymxs_node(node) xform_attrs = [ max_constants.TRANSLATION_ATTR_NAME, max_constants.ROTATION_ATTR_NAME, max_constants.SCALE_ATTR_NAME ] for xform_attr in xform_attrs: if xform_attr in attribute_name: xform = attribute_name[:-1] axis = attribute_name[-1] if axis.lower() not in max_constants.AXES: continue axis_index = max_constants.AXES.index(axis.lower()) xform_controller = rt.getPropertyController(node.controller, xform) # TODO: For now we only support default transform controllers (Bezier Float for translation, # TODO: Euler_XYZ for rotation and Bezier Scale for scale). Support other controller types. if xform == max_constants.TRANSLATION_ATTR_NAME or xform == max_constants.ROTATION_ATTR_NAME: xform_channel = rt.getPropertyController( xform_controller, '{} {}'.format(axis.lower(), xform)) xform_channel.value = attribute_value return elif xform == max_constants.SCALE_ATTR_NAME: current_scale = xform_controller.value current_scale[axis_index] = attribute_value xform_controller.value = current_scale return try: return rt.setProperty(node, attribute_name, attribute_value) except Exception: pass
def unlock_scale_attributes(node): """ Unlocks all scale transform attributes of the given node :param node: str """ node = node_utils.get_pymxs_node(node) lock_flags = list(rt.getTransformLockFlags(node)) lock_flags[6] = False lock_flags[7] = False lock_flags[8] = False to_bit_array = list() for i, elem in enumerate(lock_flags): if elem: to_bit_array.append(i + 1) ms_array = helpers.convert_python_list_to_maxscript_bit_array(to_bit_array) return rt.setTransformLockFlags(node, ms_array)
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_bone(root_node, name='new_bone', target_node=None, parent=None, **kwargs): length = kwargs.pop('length', 10) width = kwargs.pop('width', 10) height = kwargs.pop('height', 10) color = kwargs.pop('color', [7, 7, 11]) main_axis = kwargs.pop('main_axis', 'z') flip_main_axis = kwargs.pop('flip_main_axis', False) main_rot_axis = kwargs.pop('main_rot_axis', 'y') flip_main_rot_axis = kwargs.pop('flip_main_rot_axis', False) freeze = kwargs.pop('freeze', False) root_node = node_utils.get_pymxs_node(root_node) target_node = node_utils.get_pymxs_node(target_node) parent = node_utils.get_pymxs_node(parent) if not root_node or not node_utils.node_exists(root_node.name): logger.error('Bone could not be created!') return False if color and rt.classOf(color) != rt.color: color = rt.color(*color) if not color: color = rt.color(7, 7, 11) bone_name = rt.uniquename(name) bone_box = rt.box(name=bone_name, lengthsegs=1, widthsegs=1, heightsegs=1, length=length, width=width, height=height, mapcoords=True, isSelected=True) bone_box.wirecolor = color bone_box.boneEnable = True bone_box.boneAxis = rt.Name(main_axis.lower()) main_axis_index = 0 main_rot_index = 0 if main_axis.lower() == 'y': main_axis_index = 1 elif main_axis.lower() == 'z': main_axis_index = 2 if main_rot_axis.lower() == 'y': main_rot_index = 1 elif main_rot_axis.lower() == 'z': main_rot_index = 2 xform_utils.match_transforms(bone_box, root_node) if target_node and node_utils.node_exists(target_node.name): temp_helper = rt.Point(pos=rt.Point3(0, 0, 0)) xform_utils.match_position(temp_helper, bone_box) look_at_controller = rt.LookAt_Constraint() rt.setPropertyController(temp_helper.controller, 'Rotation', look_at_controller) look_at_controller.appendTarget(target_node, 1) look_at_controller.target_axis = main_axis_index look_at_controller.upnode_axis = 1 look_at_controller.StoUP_axis = main_rot_index look_at_controller.target_axisFlip = flip_main_axis look_at_controller.StoUP_axisFlip = flip_main_rot_axis xform_utils.match_transforms(bone_box, temp_helper) rt.delete(temp_helper) bone_box.height = rt.distance(root_node, target_node) if parent: bone_box.parent = parent if freeze: xform_utils.freeze_transform(bone_box) return bone_box
def create_end_bone(parent_bone, name='newBone', snap_to=None, match_to=None, parent=None, **kwargs): """ Creates and end bone :param parent_bone: :param snap_to: :param match_to: :param name: :param parent: :return: """ length = kwargs.pop('length', 10) width = kwargs.pop('width', 10) height = kwargs.pop('height', 5) color = kwargs.pop('color', [7, 7, 11]) main_axis = kwargs.pop('main_axis', 'z') extrude = kwargs.pop('extrude', True) freeze = kwargs.pop('freeze', False) parent_bone = node_utils.get_pymxs_node(parent_bone) snap_to = node_utils.get_pymxs_node(snap_to) match_to = node_utils.get_pymxs_node(match_to) parent = node_utils.get_pymxs_node(parent) if not parent_bone or not node_utils.node_exists(parent_bone.name): logger.error('Bone could not be created!') return False if color and rt.classOf(color) != rt.color: color = rt.color(*color) if not color: color = rt.color(7, 7, 11) bone_name = rt.uniquename(name) bone_box = rt.box(name=bone_name, lengthsegs=1, widthsegs=1, heightsegs=1, length=length, width=width, height=height, mapcoords=True, isSelected=True) bone_box.wirecolor = color bone_box.boneEnable = True bone_box.boneAxis = rt.Name(main_axis.lower()) xform_utils.match_transforms(bone_box, parent_bone) rt.SetCommandPanelTaskMode(rt.Name('modify')) rt.convertTo(bone_box, rt.PolyMeshObject) rt.subobjectLevel = 1 vertex_bitarray = rt.BitArray() vertex_indices = [1, 2, 3, 4] vertex_bitarray.count = len(vertex_indices) for i, index in enumerate(vertex_indices): vertex_bitarray[i] = index bone_box.EditablePoly.SetSelection(rt.Name('Vertex'), vertex_bitarray) bone_box.EditablePoly.collapse(rt.Name('Vertex')) bone_box.EditablePoly.SetSelection(rt.Name('Vertex'), rt.BitArray()) if extrude: rt.subobjectLevel = 4 vertex_bitarray = rt.BitArray() vertex_indices = [1] vertex_bitarray.count = len(vertex_indices) for i, index in enumerate(vertex_indices): vertex_bitarray[i] = index bone_box.EditablePoly.SetSelection(rt.Name('Face'), vertex_bitarray) bone_box.EditablePoly.extrudeFaces(height) bone_box.EditablePoly.SetSelection(rt.Name('Vertex'), rt.BitArray()) rt.subobjectLevel = 0 if snap_to: xform_utils.match_position(bone_box, snap_to) if match_to: xform_utils.match_transforms(bone_box, match_to) if parent: bone_box.parent = parent if freeze: xform_utils.freeze_transform(bone_box) return bone_box