def getMatrixFromVector(forward, up=None, forward_axis='z', location=None): """ Creates a rotation from given forward direction and up direction. Which is then used to calculate a matrix. Args: forward (Any): Vector that faces forward. up (Any): Vector that faces up. If none given will use scene up axis. forward_axis (string): What local vector will face forward in the matrix. location (Any): Where the matrix should be in world space. If none given, will be at origin. Returns: (pm.nodetypes.Matrix): Matrix calculated from given forward vector. """ scene_up = pm.upAxis(axis=True, q=True) scene_up = convert.axisToVector(scene_up) up = convert.toVector(up, invalid_default=scene_up) right = forward.cross(up).normal() up = right.cross(forward).normal() matrix = [] if forward_axis == 'x': matrix = [forward.get(), up.get(), right.get()] elif forward_axis == 'y': matrix = [up.get(), forward.get(), right.get()] elif forward_axis == 'z': matrix = [(right * -1).get(), up.get(), forward.get()] location = convert.toVector(location, invalid_default=True) matrix.append(location) return pm.dt.Matrix(*matrix)
def orientChainWithUpObjects(transform_start=None, up_start=None, aim=None, up_axis=None): """ Orients the given transforms by aiming the given axis to their child and the up to the respective up transform. Args: transform_start (pm.nodetypes.Transform): Start of the chain to orient. up_start (pm.nodetypes.Transform): Start of the chain that transforms will have their up axis aim at. aim (list): Axis vector to aim towards child joint. up_axis (list): Axis vector to aim towards the up object. """ if not transform_start: transform_start = pm.selected()[0] if not up_start: up_start = pm.selected()[-1] if transform_start == up_start: pm.error('Please select more than one transform!') aim = convert.toVector(aim, invalid_default=[0, 1, 0]) up_axis = convert.toVector(up_axis, invalid_default=[1, 0, 0]) transforms = transform_start.getChildren(ad=True) uppers = up_start.getChildren(ad=True) transforms.append(transform_start) uppers.append(up_start) transforms.reverse() uppers.reverse() pm.parent(transforms, w=True) for i, (transform, up) in enumerate(zip(transforms, uppers)): if i != 0: pm.parent(transform, transforms[i - 1]) if transform.hasAttr('jointOrient'): transform.jointOrient.set((0, 0, 0)) if transform == transforms[-1]: continue constraint = pm.aimConstraint(transforms[i + 1], transform, aim=aim, u=up_axis, wuo=up, mo=False, wut='object') pm.delete(constraint)
def orientChain(start=None, end=None, aim=None, up=None, world_up=None): """ Orients the chain starting from given start and ending at given end to aim at the child joint with the given aim axis. Args: start (pm.nodetypes.Transform): Top parent of the chain. end (pm.nodetypes.Transform): The child to end search at. If none given, will return only the given start. aim (list): Axis to aim at up (list): Axis to aim at the given world direction. world_up (list): Axis up will try to aim at. """ if not start: start = pm.selected()[0] if not end: end = pm.selected()[-1] if start == end: pm.error('Please select more than one transform!') transforms = getChain(start, end) first_parent = transforms[0].getParent() aim = convert.toVector(aim, invalid_default=[1, 0, 0]) up = convert.toVector(up, invalid_default=[1, 0, 0]) world_up = convert.toVector(world_up, invalid_default=[1, 0, 0]) pm.parent(transforms, w=True) for i, transform in enumerate(transforms): if i == 0: if first_parent: pm.parent(transform, first_parent) else: pm.parent(transform, transforms[i - 1]) if transform.hasAttr('jointOrient'): transform.jointOrient.set((0, 0, 0)) if transform == transforms[-1]: continue constraint = pm.aimConstraint(transforms[i + 1], transform, aim=aim, u=up, wu=world_up, mo=False, wut='vector') pm.delete(constraint)
def getDirection(start, end): """ Gets the vector direction aiming between the given start and end. Args: start (Any): Transform or vector where direction should start from. end (Any): Transform or vector where direction should end/aim at. Returns: (pm.dt.Vector): Direction aiming from start to end normalized. """ start_location = convert.toVector(start) end_location = convert.toVector(end) aim_direction = (end_location - start_location).normal() return aim_direction
def getDistance(start, end): """ Gets the distance between given start and end. Args: start (Any): Start point to get distance to end. end (Any): End point to get distance from end. Returns: (float): Distance from start point to end point. """ start_location = convert.toVector(start) end_location = convert.toVector(end) return (end_location - start_location).length()
def calculatePoleVector(start_transform, mid_transform, end_transform, scale=1, forward=[0, 0, 1]): """ Props to Greg Hendrix for walking through the math: https://vimeo.com/240328348 Args: start_transform (PyNode): Start of the joint chain mid_transform (PyNode): Mid joint of the chain. end_transform (PyNode): End joint of the chain. scale (float): How far away should the pole vector position be compared to the chain length. forward (Any): Node or list to convert to vector direction to move pole vector if chain is in straight line. Returns: (list): Transform of pole vector control. Translation as first index, rotation as second, scale as third. """ is_in_straight_line = False zero_vector = pm.dt.Vector(0, 0, 0) # if transform has rotations or joint orient, then its not in a straight line and we can figure out pole vector if mid_transform.r.get().isEquivalent(zero_vector, tol=0.5): if mid_transform.hasAttr('orientJoint'): if mid_transform.orientJoint.get().isEquivalent(zero_vector, tol=0.5): is_in_straight_line = True else: is_in_straight_line = True # if transform is in a straight line use the location of the mid transform to place the pole vector if is_in_straight_line: translation = pm.dt.Vector(pm.xform(mid_transform, q=True, ws=True, rp=True)) rotation = pm.xform(mid_transform, q=True, ws=True, ro=True) if forward: forward = convert.toVector(forward) distance = pipermath.getDistance(start_transform, end_transform) forward = forward * (distance * scale) translation = translation + forward else: start = pm.dt.Vector(pm.xform(start_transform, q=True, ws=True, rp=True)) mid = pm.dt.Vector(pm.xform(mid_transform, q=True, ws=True, rp=True)) end = pm.dt.Vector(pm.xform(end_transform, q=True, ws=True, rp=True)) start_to_end = end - start start_to_mid = mid - start start_to_end_length = start_to_end.length() mid_scale = start_to_end.dot(start_to_mid) / (start_to_end_length * start_to_end_length) mid_chain_point = start + start_to_end * mid_scale chain_length = (mid - start).length() + (end - mid).length() translation = (mid - mid_chain_point).normal() * chain_length * scale + mid rotation = pipermath.getAimRotation(mid_chain_point, translation) single_scale = mid_transform.radius.get() if mid_transform.hasAttr('radius') else 1 scale = [single_scale, single_scale, single_scale] return translation, rotation, scale, is_in_straight_line
def getMatrixFromVector(forward, up=None, forward_axis='z', location=None): """ Creates a rotation from given forward direction and up direction. Which is then used to calculate a matrix. Args: forward (Any): Vector that faces forward. up (Any): Vector that faces up. If none given will use scene up axis. forward_axis (string): What local vector will face forward in the matrix. location (Any): Where the matrix should be in world space. If none given, will be at origin. Returns: (pm.nodetypes.Matrix): Matrix calculated from given forward vector. """ if isinstance(up, (list, tuple)): up = pm.dt.Vector(up) elif isinstance(up, str): up = pm.dt.Vector(convert.axisToVector(up)) elif isinstance(up, pm.dt.Vector): pass else: up = pm.upAxis(axis=True, q=True) up = pm.dt.Vector(convert.axisToVector(up)) right = forward.cross(up).normal() up = right.cross(forward).normal() coordinates = [] if forward_axis == 'x': coordinates = [forward.get(), up.get(), right.get()] elif forward_axis == 'y': coordinates = [up.get(), forward.get(), right.get()] elif forward_axis == 'z': coordinates = [(right * -1).get(), up.get(), forward.get()] location = convert.toVector(location, invalid_zero=True) coordinates.append(location) return pm.dt.Matrix(*coordinates)