Updated
							
								
								
									
										5
									
								
								Scripts/Animation/springmagic/HowToCompile.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,5 @@
 | 
			
		||||
If you changed the source code, you have to recompile SpringMagic project
 | 
			
		||||
 | 
			
		||||
Uncomment this line in file springMagic.py
 | 
			
		||||
# Recompile SpringMagic module if modification has been made
 | 
			
		||||
dev.refresh(springmagic)
 | 
			
		||||
							
								
								
									
										30
									
								
								Scripts/Animation/springmagic/HowToInstall.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,30 @@
 | 
			
		||||
1. Unzip all files into same folder
 | 
			
		||||
 | 
			
		||||
2. Run Maya
 | 
			
		||||
 | 
			
		||||
3. Run command below in command bar with Python way
 | 
			
		||||
    execfile(r'the folder path\springMagic.py')
 | 
			
		||||
    example:
 | 
			
		||||
    execfile(r'C:\Users\Chris\Scripts\springMagic.py')
 | 
			
		||||
 | 
			
		||||
    keep the 'r' in front of your path to avoid IO Error may cause
 | 
			
		||||
 | 
			
		||||
Tested under Maya 2011 and above, you may need to install Pymel for Maya 2010 or elder build by yourself
 | 
			
		||||
 | 
			
		||||
Known Issue:
 | 
			
		||||
1.  Error: Syntax error
 | 
			
		||||
    Please check if you run the command in Python mode, you can click "mel" word to toggle between mel and python
 | 
			
		||||
 | 
			
		||||
2.  # Error: ImportError: No module named pymel.core #
 | 
			
		||||
    That means you are using old version of MAYA and have no Pymel installed, please download and install Pymel follow this page:
 | 
			
		||||
    http://download.autodesk.com/us/maya/2011help/PyMel/install.html
 | 
			
		||||
 | 
			
		||||
3.  Error: IOError: file <maya console> line 1: 2
 | 
			
		||||
    It mean your path has some error when you run the execfile command, please double check what you typed
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
for more details visit my site
 | 
			
		||||
www.animbai.com
 | 
			
		||||
 | 
			
		||||
Yanbin
 | 
			
		||||
2017.10
 | 
			
		||||
							
								
								
									
										2
									
								
								Scripts/Animation/springmagic/Howto.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,2 @@
 | 
			
		||||
import springmagic
 | 
			
		||||
springmagic.main()
 | 
			
		||||
							
								
								
									
										57
									
								
								Scripts/Animation/springmagic/UtilityFunctions.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,57 @@
 | 
			
		||||
############################################
 | 
			
		||||
# Utility Functions
 | 
			
		||||
############################################
 | 
			
		||||
 | 
			
		||||
# to get object parrent
 | 
			
		||||
# parent = obj.getParent()
 | 
			
		||||
 | 
			
		||||
# to get all parents of a joint
 | 
			
		||||
# parentList = joint.getAllParents()
 | 
			
		||||
 | 
			
		||||
# to get root bone
 | 
			
		||||
# rootBone = joint.root()
 | 
			
		||||
 | 
			
		||||
# to get object all children
 | 
			
		||||
# children = pm.listRelatives(obj, allDescendents = 1)
 | 
			
		||||
 | 
			
		||||
# to make sure the selection is a mesh
 | 
			
		||||
# pm.nodeType(pm.ls(sl=True, type='transform')[0].getShape()) == 'mesh'
 | 
			
		||||
 | 
			
		||||
# to get vertex in selection as flatten
 | 
			
		||||
# pm.ls(sl=True, type='float3', flatten=True)[0]
 | 
			
		||||
 | 
			
		||||
# to get skin cluster
 | 
			
		||||
# pm.listHistory(pm.ls(sl=True), type='skinCluster')[0]
 | 
			
		||||
 | 
			
		||||
# to get all influcent bone of a skin cluster
 | 
			
		||||
# obj.getInfluence()
 | 
			
		||||
 | 
			
		||||
# About path module
 | 
			
		||||
 | 
			
		||||
# from pymel.util.path import path
 | 
			
		||||
# filePath = 'c:/temp/test/myTestFile.txt'
 | 
			
		||||
# fpPathObj = path(filePath)
 | 
			
		||||
# fpPathObj
 | 
			
		||||
# # Result: path('c:/temp/test/myTestFile.txt') #
 | 
			
		||||
# fpPathObj.basename()
 | 
			
		||||
# # Result: 'myTestFile.txt' #
 | 
			
		||||
# # .name is a property which returns the same
 | 
			
		||||
# fpPathObj.name
 | 
			
		||||
# # Result: 'myTestFile.txt' #
 | 
			
		||||
# # namebase returns fileName only w/o extension
 | 
			
		||||
# fpPathObj.namebase
 | 
			
		||||
# # Result: 'myTestFile' #
 | 
			
		||||
# # return directory above file
 | 
			
		||||
# fpPathObj.parent
 | 
			
		||||
# # Result: path('c:/temp/test') #
 | 
			
		||||
# # check extension
 | 
			
		||||
# fpPathObj.endswith('txt')
 | 
			
		||||
# # Result: True #
 | 
			
		||||
# # check existance
 | 
			
		||||
# fpPathObj.exists()
 | 
			
		||||
# # Result: True #
 | 
			
		||||
# # check to see if folder type
 | 
			
		||||
# fpPathObj.parent.isdir()
 | 
			
		||||
# # Result: True #
 | 
			
		||||
# fpPathObj.parent.parent.name
 | 
			
		||||
# # Result: 'temp' #
 | 
			
		||||
							
								
								
									
										11
									
								
								Scripts/Animation/springmagic/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,11 @@
 | 
			
		||||
# from springmagic.main import main
 | 
			
		||||
 | 
			
		||||
__version__ = "3.5a"
 | 
			
		||||
 | 
			
		||||
def version():
 | 
			
		||||
    """
 | 
			
		||||
    Return the current version of the Spring Magic
 | 
			
		||||
 | 
			
		||||
    :rtype: str
 | 
			
		||||
    """
 | 
			
		||||
    return __version__
 | 
			
		||||
							
								
								
									
										942
									
								
								Scripts/Animation/springmagic/core.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,942 @@
 | 
			
		||||
# - * - coding: utf - 8 - * -
 | 
			
		||||
# PEP8 formatting
 | 
			
		||||
 | 
			
		||||
#####################################################################################
 | 
			
		||||
#
 | 
			
		||||
# Spring Magic for Maya
 | 
			
		||||
#
 | 
			
		||||
# Calculate bone chain animation by settings, support collisions and wind force
 | 
			
		||||
# Can work with rigging controller as well
 | 
			
		||||
#
 | 
			
		||||
# Need pringMagic.ui file to work with
 | 
			
		||||
# This script need also icon file support, which should be put in same folder
 | 
			
		||||
#
 | 
			
		||||
# feel free to mail me redtank@outlook.com for any bug or issue
 | 
			
		||||
#
 | 
			
		||||
# Yanbin Bai
 | 
			
		||||
# 2021.02
 | 
			
		||||
#
 | 
			
		||||
#####################################################################################
 | 
			
		||||
 | 
			
		||||
import math
 | 
			
		||||
import logging
 | 
			
		||||
# import copy
 | 
			
		||||
 | 
			
		||||
import pymel.core as pm
 | 
			
		||||
import pymel.core.datatypes as dt
 | 
			
		||||
import maya.cmds as cmds
 | 
			
		||||
 | 
			
		||||
import Animation.springmagic.decorators as decorators
 | 
			
		||||
import Animation.springmagic.springMath as springMath
 | 
			
		||||
 | 
			
		||||
from Animation.springmagic.utility import *
 | 
			
		||||
 | 
			
		||||
from itertools import cycle
 | 
			
		||||
from itertools import chain
 | 
			
		||||
from weakref import WeakValueDictionary
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
###################################
 | 
			
		||||
# spring magic
 | 
			
		||||
####################################
 | 
			
		||||
 | 
			
		||||
kWindObjectName = 'spring_wind'
 | 
			
		||||
kSpringProxySuffix = '_SpringProxy'
 | 
			
		||||
kCollisionPlaneSuffix = '_SpringColPlane'
 | 
			
		||||
kCapsuleNameSuffix = '_collision_capsule'
 | 
			
		||||
kNullSuffix = '_SpringNull'
 | 
			
		||||
# kTwistNullSuffix = '_SpringTwistNull'
 | 
			
		||||
 | 
			
		||||
class Spring:
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ratio=0.5, twistRatio=0.0, tension=0.0, extend=0.0, inertia=0.0):
 | 
			
		||||
 | 
			
		||||
        self.ratio = ratio
 | 
			
		||||
        self.twist_ratio = twistRatio
 | 
			
		||||
        self.tension = tension
 | 
			
		||||
        self.extend = extend
 | 
			
		||||
        self.inertia = inertia
 | 
			
		||||
 | 
			
		||||
class SpringMagic:
 | 
			
		||||
 | 
			
		||||
    def __init__(self, startFrame, endFrame, subDiv=1.0, isLoop=False, isPoseMatch=False, isCollision=False, isFastMove=False, wipeSubframe=True):
 | 
			
		||||
 | 
			
		||||
        self.start_frame = startFrame
 | 
			
		||||
        self.end_frame = endFrame
 | 
			
		||||
 | 
			
		||||
        self.sub_div = subDiv
 | 
			
		||||
        self.is_loop = isLoop
 | 
			
		||||
        self.is_pose_match = isPoseMatch
 | 
			
		||||
        self.is_fast_move = isFastMove
 | 
			
		||||
        self.wipe_subframe = wipeSubframe
 | 
			
		||||
 | 
			
		||||
        self.is_collision = isCollision
 | 
			
		||||
        self.collision_planes_list = None
 | 
			
		||||
 | 
			
		||||
        self.wind = None
 | 
			
		||||
 | 
			
		||||
class SpringData:
 | 
			
		||||
 | 
			
		||||
    cur_position_locator = None
 | 
			
		||||
    prev_position_locator = None
 | 
			
		||||
    prev_grand_child_position_locator = None
 | 
			
		||||
 | 
			
		||||
    _instances = WeakValueDictionary()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def Count(self):
 | 
			
		||||
        return len(self._instances)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, springMagic, spring, transform, child, grand_child, grand_parent):
 | 
			
		||||
        # self.current_child_position
 | 
			
		||||
 | 
			
		||||
        self._instances[id(self)] = self
 | 
			
		||||
 | 
			
		||||
        self.springMagic = springMagic
 | 
			
		||||
        self.spring = spring
 | 
			
		||||
 | 
			
		||||
        self.parent = transform
 | 
			
		||||
        self.child = child
 | 
			
		||||
        self.grand_child = grand_child
 | 
			
		||||
        self.grand_parent = grand_parent
 | 
			
		||||
 | 
			
		||||
        self.child_position = get_translation(child)
 | 
			
		||||
        self.grand_child_position = get_translation(grand_child) if grand_child else None
 | 
			
		||||
 | 
			
		||||
        self.previous_child_position = self.child_position
 | 
			
		||||
 | 
			
		||||
        self.rotation = get_rotation(transform)
 | 
			
		||||
        self.up_vector = get_matrix(transform)[4:7]
 | 
			
		||||
 | 
			
		||||
        transform_pos = get_translation(transform)
 | 
			
		||||
        self.bone_length = springMath.distance(transform_pos, self.child_position)
 | 
			
		||||
 | 
			
		||||
        self.has_child_collide = False
 | 
			
		||||
        self.has_plane_collide = False
 | 
			
		||||
 | 
			
		||||
        # create temporary locators use for aim constraint
 | 
			
		||||
        if not SpringData.cur_position_locator:
 | 
			
		||||
            SpringData.cur_position_locator = pm.spaceLocator(name='cur_position_locator')
 | 
			
		||||
 | 
			
		||||
        if not SpringData.prev_position_locator:
 | 
			
		||||
            SpringData.prev_position_locator = pm.spaceLocator(name='prev_position_locator')
 | 
			
		||||
 | 
			
		||||
        if not SpringData.prev_grand_child_position_locator:
 | 
			
		||||
            SpringData.prev_grand_child_position_locator = pm.spaceLocator(name='prev_grand_child_position_locator')
 | 
			
		||||
 | 
			
		||||
        # Weight attribute to de/activate the aim constraint in pose match mode
 | 
			
		||||
        self.pairblend_weight_attribute = None
 | 
			
		||||
        self.aim_constraint = None
 | 
			
		||||
 | 
			
		||||
        self.__create_child_proxy()
 | 
			
		||||
        self.__prepare_animation_key()
 | 
			
		||||
        self.__create_aim_constraint()
 | 
			
		||||
        self.__init_pairblend_weight()
 | 
			
		||||
 | 
			
		||||
    def __del__(self):
 | 
			
		||||
 | 
			
		||||
        if self.Count == 0:
 | 
			
		||||
            # print('Last Counter object deleted')
 | 
			
		||||
 | 
			
		||||
            # delete temporary locators (useful, it's delete constraints at the same time)
 | 
			
		||||
            pm.delete(SpringData.cur_position_locator, SpringData.prev_position_locator, SpringData.prev_grand_child_position_locator)
 | 
			
		||||
 | 
			
		||||
            SpringData.cur_position_locator = None
 | 
			
		||||
            SpringData.prev_position_locator = None
 | 
			
		||||
            SpringData.prev_grand_child_position_locator = None
 | 
			
		||||
 | 
			
		||||
            # remove all spring nulls, add recursive incase name spaces
 | 
			
		||||
            # pm.delete(pm.ls('*' + kNullSuffix + '*', recursive=1))
 | 
			
		||||
        else:
 | 
			
		||||
            # print(self.Count, 'Counter objects remaining')
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        if self.child_proxy:
 | 
			
		||||
            # remove spring nulls, add recursive incase name spaces
 | 
			
		||||
            pm.delete(pm.ls('*' + self.child_proxy + '*', recursive=1))
 | 
			
		||||
 | 
			
		||||
    def update(self, has_collision, has_hit_plane, child_pos_corrected):
 | 
			
		||||
        # Update current transform with the new values
 | 
			
		||||
        self.child_position = get_translation(self.child)
 | 
			
		||||
        self.grand_child_position = get_translation(self.grand_child) if self.grand_child else None
 | 
			
		||||
        self.previous_child_position = child_pos_corrected
 | 
			
		||||
 | 
			
		||||
        self.rotation = get_rotation(self.parent)
 | 
			
		||||
        self.up_vector = get_matrix(self.parent)[4:7]
 | 
			
		||||
 | 
			
		||||
        self.has_child_collide = has_collision
 | 
			
		||||
        self.has_plane_collide = has_hit_plane
 | 
			
		||||
 | 
			
		||||
    def __create_child_proxy(self):
 | 
			
		||||
        # create a null at child pos, then parent to obj parent for calculation
 | 
			
		||||
        child_proxy_locator_name = self.parent.name() + kNullSuffix
 | 
			
		||||
        child_proxy_list = pm.ls(child_proxy_locator_name)
 | 
			
		||||
 | 
			
		||||
        if not child_proxy_list:
 | 
			
		||||
            self.child_proxy = pm.spaceLocator(name=child_proxy_locator_name)
 | 
			
		||||
        else:
 | 
			
		||||
            self.child_proxy = child_proxy_list[0]
 | 
			
		||||
 | 
			
		||||
        self.child_proxy.getShape().setAttr('visibility', False)
 | 
			
		||||
 | 
			
		||||
        pm.parent(self.child_proxy, self.parent.getParent())
 | 
			
		||||
        # pm.parent(child_proxy, self.grand_parent)
 | 
			
		||||
 | 
			
		||||
        if not self.springMagic.is_pose_match:
 | 
			
		||||
            self.child_proxy.setTranslation(self.child.getTranslation(space='world'), space='world')
 | 
			
		||||
            self.child_proxy.setRotation(self.child.getRotation(space='world'), space='world')
 | 
			
		||||
 | 
			
		||||
    def __prepare_animation_key(self):
 | 
			
		||||
        if not self.springMagic.is_pose_match:
 | 
			
		||||
            # remove exists keys
 | 
			
		||||
            pm.cutKey(self.parent, time=(self.springMagic.start_frame, self.springMagic.end_frame + 0.99999))
 | 
			
		||||
            pm.cutKey(self.child, time=(self.springMagic.start_frame, self.springMagic.end_frame + 0.99999))
 | 
			
		||||
 | 
			
		||||
            # set key
 | 
			
		||||
            pm.setKeyframe(self.parent, attribute='rotate')
 | 
			
		||||
 | 
			
		||||
            if self.spring.extend != 0.0:
 | 
			
		||||
                pm.setKeyframe(self.child, attribute='tx')
 | 
			
		||||
 | 
			
		||||
    def __create_aim_constraint(self):
 | 
			
		||||
        # Create a constraint per transform to speed up computation, not active yet (weight=0)
 | 
			
		||||
        self.aim_constraint = pm.aimConstraint(SpringData.cur_position_locator, SpringData.prev_position_locator, SpringData.prev_grand_child_position_locator, self.parent, aimVector=[1, 0, 0], upVector=[0, 1, 0], maintainOffset=False, weight=0)
 | 
			
		||||
 | 
			
		||||
    def __init_pairblend_weight(self):
 | 
			
		||||
        # if transform rotation has no animation, set a key at start frame to force the creation of a pairblend when the aim constraint is created
 | 
			
		||||
        for rotation_input in ['rx', 'ry', 'rz']:
 | 
			
		||||
            rotation_connection = pm.listConnections(self.parent + '.' + rotation_input, d=False, s=True)
 | 
			
		||||
 | 
			
		||||
            if not rotation_connection:
 | 
			
		||||
                pm.setKeyframe(self.parent, attribute=rotation_input)
 | 
			
		||||
 | 
			
		||||
        pairblends = pm.listConnections(self.parent, type="pairBlend", destination=True, skipConversionNodes=True)
 | 
			
		||||
 | 
			
		||||
        # Find the pairblend connected to the aim constraint
 | 
			
		||||
        for pairblend in pairblends:
 | 
			
		||||
 | 
			
		||||
            connected_constraint_list = cmds.listConnections(pairblend.name(), type='constraint', destination=False)
 | 
			
		||||
 | 
			
		||||
            if self.aim_constraint.name() in connected_constraint_list:
 | 
			
		||||
 | 
			
		||||
                # Get pairblend weight connected attribute
 | 
			
		||||
                # return [u'joint2.blendAim1')]
 | 
			
		||||
                weight_attribute_list = cmds.listConnections(pairblend + '.weight', d=False, s=True, p=True)
 | 
			
		||||
 | 
			
		||||
                if weight_attribute_list:
 | 
			
		||||
                    self.pairblend_weight_attribute = weight_attribute_list[0]
 | 
			
		||||
 | 
			
		||||
    def set_pairblend_weight(self, blend_value):
 | 
			
		||||
        if self.pairblend_weight_attribute:
 | 
			
		||||
            pm.setAttr(self.pairblend_weight_attribute, blend_value)
 | 
			
		||||
 | 
			
		||||
    def keyframe_child_proxy(self):
 | 
			
		||||
 | 
			
		||||
        if self.child_proxy:
 | 
			
		||||
            # Deactivate pairblend weight
 | 
			
		||||
            # Aim constraint weight set to 0 is not enough, it paratizes the process
 | 
			
		||||
            self.set_pairblend_weight(0.0)
 | 
			
		||||
 | 
			
		||||
            self.child_proxy.setTranslation(self.child.getTranslation(space='world'), space='world')
 | 
			
		||||
            pm.setKeyframe(self.child_proxy, attribute='translate')
 | 
			
		||||
            self.child_proxy.setRotation(self.child.getRotation(space='world'), space='world')
 | 
			
		||||
            pm.setKeyframe(self.child_proxy, attribute='rotate')
 | 
			
		||||
 | 
			
		||||
            self.set_pairblend_weight(1.0)
 | 
			
		||||
 | 
			
		||||
    def apply_inertia(self, currentChildPosition):
 | 
			
		||||
        ratio = self.spring.ratio / self.springMagic.sub_div
 | 
			
		||||
        inertia_offset = [0.0, 0.0, 0.0]
 | 
			
		||||
 | 
			
		||||
        if self.spring.inertia > 0.0:
 | 
			
		||||
            bone_ref_loc_offset_dir = currentChildPosition - self.child_position
 | 
			
		||||
            bone_ref_loc_offset_distance = ((bone_ref_loc_offset_dir) * (1 - ratio) * (1 - self.spring.inertia)).length()
 | 
			
		||||
 | 
			
		||||
            inertia_offset = bone_ref_loc_offset_dir.normal() * (bone_ref_loc_offset_distance / self.springMagic.sub_div)
 | 
			
		||||
 | 
			
		||||
        # apply mass
 | 
			
		||||
        force_direction = self.child_position - self.previous_child_position
 | 
			
		||||
        force_distance = force_direction.length() * self.spring.inertia
 | 
			
		||||
 | 
			
		||||
        # offset position
 | 
			
		||||
        inertia_offset += force_direction.normal() * (force_distance / self.springMagic.sub_div)
 | 
			
		||||
 | 
			
		||||
        return inertia_offset
 | 
			
		||||
 | 
			
		||||
    def apply_wind(self, frame):
 | 
			
		||||
        wind_offset = [0.0, 0.0, 0.0]
 | 
			
		||||
 | 
			
		||||
        if self.springMagic.wind:
 | 
			
		||||
            wind_max_force = self.springMagic.wind.getAttr('MaxForce')
 | 
			
		||||
            wind_min_force = self.springMagic.wind.getAttr('MinForce')
 | 
			
		||||
            wind_frequency = self.springMagic.wind.getAttr('Frequency')
 | 
			
		||||
 | 
			
		||||
            mid_force = (wind_max_force + wind_min_force) / 2
 | 
			
		||||
 | 
			
		||||
            # get source x - axis direction in world space
 | 
			
		||||
            wind_direction = get_matrix(self.springMagic.wind)[:3]
 | 
			
		||||
            # sDirection = sObj.getMatrix()[0][:3]
 | 
			
		||||
            wind_direction = dt.Vector(wind_direction[0], wind_direction[1], wind_direction[2]).normal()
 | 
			
		||||
            wind_distance = math.sin(frame * wind_frequency) * (wind_max_force - wind_min_force) + mid_force
 | 
			
		||||
 | 
			
		||||
            # offset position
 | 
			
		||||
            wind_offset = wind_direction.normal() * wind_distance
 | 
			
		||||
 | 
			
		||||
        return wind_offset
 | 
			
		||||
 | 
			
		||||
    def detect_collision(self, new_obj_pos, new_child_pos, capsule_list):
 | 
			
		||||
        col_pre = col_cur = None
 | 
			
		||||
 | 
			
		||||
        child_pos_corrected = self.child_position
 | 
			
		||||
 | 
			
		||||
        if self.springMagic.is_collision and capsule_list:
 | 
			
		||||
 | 
			
		||||
            if preCheckCollision(new_obj_pos, self.bone_length, capsule_list):
 | 
			
		||||
 | 
			
		||||
                # check collision from previous pos to cur pos
 | 
			
		||||
                col_pre, col_body_pre, hitCylinder_pre = springMath.checkCollision(new_child_pos, self.child_position, capsule_list, True)
 | 
			
		||||
 | 
			
		||||
                # check collision from cur pos to previous pos
 | 
			
		||||
                col_cur, col_body_cur, hitCylinder_cur = springMath.checkCollision(new_child_pos, self.child_position, capsule_list, False)
 | 
			
		||||
 | 
			
		||||
                if col_pre and (col_cur is None):
 | 
			
		||||
                    new_child_pos = col_pre
 | 
			
		||||
                elif col_cur and (col_pre is None):
 | 
			
		||||
                    child_pos_corrected = col_cur
 | 
			
		||||
                elif col_pre and col_cur:
 | 
			
		||||
 | 
			
		||||
                    # move cur child pose to closest out point if both pre and cur pos are already inside of col body
 | 
			
		||||
                    # if distance(col_pre, new_child_pos) < distance(col_cur, new_child_pos):
 | 
			
		||||
                    mid_point = (self.child_position + new_child_pos) / 2
 | 
			
		||||
 | 
			
		||||
                    if springMath.distance(col_pre, mid_point) < springMath.distance(col_cur, mid_point):
 | 
			
		||||
                        new_child_pos = col_pre
 | 
			
		||||
                    else:
 | 
			
		||||
                        new_child_pos = col_cur
 | 
			
		||||
 | 
			
		||||
                    if self.springMagic.is_fast_move:
 | 
			
		||||
                        child_pos_corrected = new_child_pos
 | 
			
		||||
 | 
			
		||||
                # # draw debug locator
 | 
			
		||||
                # if col_pre and col_cur:
 | 
			
		||||
                #     locator1 = pm.spaceLocator(name=obj.name() + '_col_pre_locator_' + str(i))
 | 
			
		||||
                #     locator1.setTranslation(col_pre)
 | 
			
		||||
                #     locator1 = pm.spaceLocator(name=obj.name() + '_col_cur_locator_' + str(i))
 | 
			
		||||
                #     locator1.setTranslation(col_cur)
 | 
			
		||||
 | 
			
		||||
        return True if col_pre or col_cur else False, new_child_pos, child_pos_corrected
 | 
			
		||||
 | 
			
		||||
    def detect_plane_hit(self, new_obj_pos, new_child_pos, grand_parent_has_plane_collision):
 | 
			
		||||
        has_hit_plane = False
 | 
			
		||||
 | 
			
		||||
        if self.springMagic.is_collision and self.springMagic.collision_planes_list[0]:
 | 
			
		||||
            collision_plane = self.springMagic.collision_planes_list[0]
 | 
			
		||||
            has_plane_collision = springMath.checkPlaneCollision(new_obj_pos, new_child_pos, collision_plane)
 | 
			
		||||
 | 
			
		||||
            if has_plane_collision or grand_parent_has_plane_collision:
 | 
			
		||||
                new_child_pos = repeatMoveToPlane(self.parent, new_child_pos, self.child, collision_plane, 3)
 | 
			
		||||
                has_hit_plane = True
 | 
			
		||||
 | 
			
		||||
        return has_hit_plane, new_child_pos
 | 
			
		||||
 | 
			
		||||
    # calculate upvector by interpolation y axis for twist
 | 
			
		||||
    def compute_up_vector(self):
 | 
			
		||||
        twist_ratio = self.spring.twist_ratio / self.springMagic.sub_div
 | 
			
		||||
 | 
			
		||||
        cur_obj_yAxis = get_matrix(self.child_proxy)[4:7]
 | 
			
		||||
        prev_up_vector = dt.Vector(self.up_vector[0], self.up_vector[1], self.up_vector[2]).normal()
 | 
			
		||||
        cur_up_vector = dt.Vector(cur_obj_yAxis[0], cur_obj_yAxis[1], cur_obj_yAxis[2]).normal()
 | 
			
		||||
 | 
			
		||||
        up_vector = (prev_up_vector * (1 - twist_ratio)) + (cur_up_vector * twist_ratio)
 | 
			
		||||
 | 
			
		||||
        return up_vector
 | 
			
		||||
 | 
			
		||||
    def aim_by_ratio(self, upVector, newChildPos, childPosCorrected):
 | 
			
		||||
        ratio = self.spring.ratio / self.springMagic.sub_div
 | 
			
		||||
        tension = self.spring.tension / (1.0 / (springMath.sigmoid(1 - self.springMagic.sub_div) + 0.5))
 | 
			
		||||
 | 
			
		||||
        # print("obj: " + str(self.parent.name()))
 | 
			
		||||
        # print("newChildPos: " + str(newChildPos))
 | 
			
		||||
        # print("childPosCorrected: " + str(childPosCorrected))
 | 
			
		||||
        # print("grand_child_position: " + str(self.grand_child_position))
 | 
			
		||||
        # print("upVector: " + str(upVector))
 | 
			
		||||
        # print("ratio: " + str(ratio))
 | 
			
		||||
        # print("tension: " + str(tension))
 | 
			
		||||
 | 
			
		||||
        SpringData.cur_position_locator.setTranslation(newChildPos)
 | 
			
		||||
        SpringData.prev_position_locator.setTranslation(childPosCorrected)
 | 
			
		||||
 | 
			
		||||
        pm.aimConstraint(self.parent, e=True, worldUpVector=upVector)
 | 
			
		||||
 | 
			
		||||
        pm.aimConstraint(SpringData.cur_position_locator, self.parent, e=True, w=ratio)
 | 
			
		||||
        pm.aimConstraint(SpringData.prev_position_locator, self.parent, e=True, w=1 - ratio)
 | 
			
		||||
 | 
			
		||||
        if self.has_child_collide and self.grand_child_position and tension != 0:
 | 
			
		||||
            SpringData.prev_grand_child_position_locator.setTranslation(self.grand_child_position)
 | 
			
		||||
            pm.aimConstraint(SpringData.prev_grand_child_position_locator, self.parent, e=True, w=(1 - ratio) * tension)
 | 
			
		||||
 | 
			
		||||
        pm.setKeyframe(self.parent, attribute='rotate')
 | 
			
		||||
 | 
			
		||||
        pm.aimConstraint(SpringData.cur_position_locator, SpringData.prev_position_locator, SpringData.prev_grand_child_position_locator, self.parent, e=True, w=0.0)
 | 
			
		||||
 | 
			
		||||
    def extend_bone(self, childPosCorrected):
 | 
			
		||||
        if self.spring.extend != 0.0:
 | 
			
		||||
            child_translation = self.child.getTranslation()
 | 
			
		||||
            # get length between bone pos and child pos
 | 
			
		||||
            x2 = (childPosCorrected - get_translation(self.parent)).length()
 | 
			
		||||
            x3 = (self.bone_length * (1 - self.spring.extend)) + (x2 * self.spring.extend)
 | 
			
		||||
            self.child.setTranslation([x3, child_translation[1], child_translation[2]])
 | 
			
		||||
            pm.setKeyframe(self.child, attribute='tx')
 | 
			
		||||
        # else:
 | 
			
		||||
        #     self.child.setTranslation([self.bone_length, child_translation[1], child_translation[2]])
 | 
			
		||||
 | 
			
		||||
def createCollisionPlane():
 | 
			
		||||
 | 
			
		||||
    # remove exist plane
 | 
			
		||||
    collision_plane = get_node('*' + kCollisionPlaneSuffix + '*')
 | 
			
		||||
 | 
			
		||||
    if collision_plane:
 | 
			
		||||
        pm.delete(collision_plane)
 | 
			
		||||
 | 
			
		||||
    collision_plane = pm.polyPlane(name="the" + kCollisionPlaneSuffix, sx=1, sy=1, w=10, h=10, ch=1)[0]
 | 
			
		||||
 | 
			
		||||
    # one side display
 | 
			
		||||
    pm.setAttr(collision_plane.doubleSided, False)
 | 
			
		||||
 | 
			
		||||
    # lock scale
 | 
			
		||||
    pm.setAttr(collision_plane.sx, lock=True)
 | 
			
		||||
    pm.setAttr(collision_plane.sy, lock=True)
 | 
			
		||||
    pm.setAttr(collision_plane.sz, lock=True)
 | 
			
		||||
 | 
			
		||||
    pm.select(collision_plane)
 | 
			
		||||
 | 
			
		||||
def removeBody(clear=False):
 | 
			
		||||
    cylinder_list = getCapsule(clear)
 | 
			
		||||
 | 
			
		||||
    pm.delete(cylinder_list)
 | 
			
		||||
 | 
			
		||||
    collision_plane = get_node('*' + kCollisionPlaneSuffix + '*')
 | 
			
		||||
 | 
			
		||||
    if collision_plane:
 | 
			
		||||
        pm.delete(collision_plane)
 | 
			
		||||
 | 
			
		||||
def addWindObj():
 | 
			
		||||
    windCone = pm.cone(name=kWindObjectName)[0]
 | 
			
		||||
 | 
			
		||||
    windCone.setScale([5, 5, 5])
 | 
			
		||||
 | 
			
		||||
    pm.delete(windCone, constructionHistory=1)
 | 
			
		||||
 | 
			
		||||
    # add wind attr
 | 
			
		||||
    pm.addAttr(windCone, longName='MaxForce', attributeType='float')
 | 
			
		||||
    pm.setAttr(windCone.name() + '.MaxForce', 1, e=1, keyable=1)
 | 
			
		||||
    pm.addAttr(windCone, longName='MinForce', attributeType='float')
 | 
			
		||||
    pm.setAttr(windCone.name() + '.MinForce', 0.5, e=1, keyable=1)
 | 
			
		||||
    pm.addAttr(windCone, longName='Frequency', attributeType='float')
 | 
			
		||||
    pm.setAttr(windCone.name() + '.Frequency', 1, e=1, keyable=1)
 | 
			
		||||
    # pm.addAttr(windCone, longName='Wave', attributeType='float')
 | 
			
		||||
    # pm.setAttr(windCone.name() + '.Wave', 0.5, e=1, keyable=1)
 | 
			
		||||
 | 
			
		||||
    setWireShading(windCone, False)
 | 
			
		||||
 | 
			
		||||
    pm.makeIdentity(apply=True)
 | 
			
		||||
    windCone.setRotation([0, 0, 90])
 | 
			
		||||
 | 
			
		||||
def bindControls(linked_chains=False):
 | 
			
		||||
    selected_ctrls = pm.ls(sl=True)
 | 
			
		||||
    pm.select(clear=True)
 | 
			
		||||
 | 
			
		||||
    # The chains are linked, we can sort them
 | 
			
		||||
    if linked_chains:
 | 
			
		||||
        # Create list for every ctrls chains
 | 
			
		||||
        # ie [[ctrl1, ctrl1.1, ctrl1.2], [ctrl2, ctrl2.1, ctrl2.2, ctrl2.3]]
 | 
			
		||||
        all_ctrls_descendants_list = pm.listRelatives(selected_ctrls, allDescendents=True)
 | 
			
		||||
        top_hierarchy_ctrls_list = [x for x in selected_ctrls if x not in all_ctrls_descendants_list]
 | 
			
		||||
 | 
			
		||||
        ctrls_chains_list = map(lambda x: [x] + [y for y in pm.listRelatives(x, allDescendents=True) if y in selected_ctrls][::-1], top_hierarchy_ctrls_list)
 | 
			
		||||
    # No sorting possible because the controlers have no lineage
 | 
			
		||||
    else:
 | 
			
		||||
        ctrls_chains_list = [selected_ctrls]
 | 
			
		||||
 | 
			
		||||
    proxy_joint_chain_list = []
 | 
			
		||||
 | 
			
		||||
    for ctrls_list in ctrls_chains_list:
 | 
			
		||||
 | 
			
		||||
        proxy_joint_list = []
 | 
			
		||||
 | 
			
		||||
        for ctrl in ctrls_list:
 | 
			
		||||
            # create proxy joint in ctrl world position
 | 
			
		||||
            ctrl_position = pm.xform(ctrl, worldSpace=1, rp=1, q=1)
 | 
			
		||||
 | 
			
		||||
            proxyJoint = pm.joint(name=ctrl.name() + kSpringProxySuffix, position=ctrl_position, radius=0.2, roo='xyz')
 | 
			
		||||
            proxy_joint_list.append(proxyJoint)
 | 
			
		||||
 | 
			
		||||
        for joint in proxy_joint_list:
 | 
			
		||||
            # set joint orientation
 | 
			
		||||
            pm.joint(joint, edit=1, orientJoint='xyz', zeroScaleOrient=True)
 | 
			
		||||
 | 
			
		||||
            # Straight bones alignment
 | 
			
		||||
            joint.setRotation([0, 0, 0])
 | 
			
		||||
            joint.setAttr('rotateAxis', [0, 0, 0])
 | 
			
		||||
            joint.setAttr('jointOrient', [0, 0, 0])
 | 
			
		||||
 | 
			
		||||
            # Free rotation (move rotation values to joint orient values)
 | 
			
		||||
            # pm.makeIdentity(proxy_joint_list[idx], apply=True, t=False, r=True, s=False, pn=True)
 | 
			
		||||
 | 
			
		||||
        if proxy_joint_list:
 | 
			
		||||
            # parent root proxy joint to control parent
 | 
			
		||||
            pm.parent(proxy_joint_list[0], ctrls_list[0].getParent())
 | 
			
		||||
 | 
			
		||||
        # Necessary to start a new joint chain
 | 
			
		||||
        pm.select(clear=True)
 | 
			
		||||
 | 
			
		||||
        proxy_joint_chain_list += [proxy_joint_list]
 | 
			
		||||
 | 
			
		||||
        for idx, joint in enumerate(proxy_joint_list[:-1]):
 | 
			
		||||
            # orient joint chain
 | 
			
		||||
            cns = pm.aimConstraint(ctrls_list[idx + 1], proxy_joint_list[idx], aimVector=[1, 0, 0], upVector=[0, 0, 0], worldUpVector=[0, 1, 0], skip='x')
 | 
			
		||||
            pm.delete(cns)
 | 
			
		||||
 | 
			
		||||
        for idx, joint in enumerate(proxy_joint_list):
 | 
			
		||||
            pm.parentConstraint(proxy_joint_list[idx], ctrls_list[idx], maintainOffset=True)
 | 
			
		||||
 | 
			
		||||
    pm.select(proxy_joint_chain_list)
 | 
			
		||||
 | 
			
		||||
def clearBind(startFrame, endFrame):
 | 
			
		||||
    proxyJointLst = pm.ls(sl=True)
 | 
			
		||||
    pm.select(d=True)
 | 
			
		||||
 | 
			
		||||
    ctrlList = []
 | 
			
		||||
 | 
			
		||||
    for bone in proxyJointLst:
 | 
			
		||||
        ctrl = pm.ls(bone.name().split(kSpringProxySuffix)[0])[0]
 | 
			
		||||
        ctrlList.append(ctrl)
 | 
			
		||||
 | 
			
		||||
    if ctrlList:
 | 
			
		||||
        pm.bakeResults(*ctrlList, t=(startFrame, endFrame))
 | 
			
		||||
 | 
			
		||||
    pm.delete(proxyJointLst)
 | 
			
		||||
 | 
			
		||||
def bindPose():
 | 
			
		||||
    pm.runtime.GoToBindPose()
 | 
			
		||||
 | 
			
		||||
# Prepare all information to call SpringMagicMaya function
 | 
			
		||||
def startCompute(spring, springMagic, progression_callback=None):
 | 
			
		||||
 | 
			
		||||
    autokeyframe_state = cmds.autoKeyframe(query=True, state=True)
 | 
			
		||||
    cmds.autoKeyframe(state=False)
 | 
			
		||||
 | 
			
		||||
    # get selection obj
 | 
			
		||||
    objs = pm.ls(sl=True)
 | 
			
		||||
 | 
			
		||||
    # check objects validity
 | 
			
		||||
    for obj in objs:
 | 
			
		||||
        # has duplicate name obj
 | 
			
		||||
        nameCntErr = (len(pm.ls(obj.name())) > 1)
 | 
			
		||||
 | 
			
		||||
        # is a duplicate obj
 | 
			
		||||
        nameValidErr = (obj.name().find('|') > 0)
 | 
			
		||||
 | 
			
		||||
        if nameCntErr or nameValidErr:
 | 
			
		||||
            raise ValueError(obj.name() + ' has duplicate name object! Stopped!')
 | 
			
		||||
 | 
			
		||||
        obj_translation = obj.getTranslation()
 | 
			
		||||
 | 
			
		||||
        if (obj_translation[0] < 0 or abs(obj_translation[1]) > 0.001 or abs(obj_translation[2]) > 0.001) and obj.getParent() and (obj.getParent() in objs):
 | 
			
		||||
            pm.warning(obj.getParent().name() + "'s X axis not point to child! May get broken result!")
 | 
			
		||||
 | 
			
		||||
    # Search for collision objects
 | 
			
		||||
    if springMagic.is_collision:
 | 
			
		||||
        springMagic.collision_planes_list = [get_node('*' + kCollisionPlaneSuffix + '*')]
 | 
			
		||||
 | 
			
		||||
    # Search for a wind object
 | 
			
		||||
    if pm.ls(kWindObjectName):
 | 
			
		||||
        springMagic.wind = pm.ls(kWindObjectName)[0]
 | 
			
		||||
 | 
			
		||||
    SpringMagicMaya(objs, spring, springMagic, progression_callback)
 | 
			
		||||
 | 
			
		||||
    cmds.autoKeyframe(state=autokeyframe_state)
 | 
			
		||||
 | 
			
		||||
# @decorators.viewportOff
 | 
			
		||||
@decorators.gShowProgress(status="SpringMagic does his magic")
 | 
			
		||||
def SpringMagicMaya(objs, spring, springMagic, progression_callback=None):
 | 
			
		||||
    # on each frame go through all objs and do:
 | 
			
		||||
    # 1. make a vectorA from current obj position to previous child position
 | 
			
		||||
    # 2. make a vectorB from current obj position to current child position
 | 
			
		||||
    # 3. calculate the angle between two vectors
 | 
			
		||||
    # 4. rotate the obj towards vectorA base on spring value
 | 
			
		||||
 | 
			
		||||
    start_frame = springMagic.start_frame
 | 
			
		||||
    end_frame = springMagic.end_frame
 | 
			
		||||
    sub_div = springMagic.sub_div
 | 
			
		||||
 | 
			
		||||
    # remove all spring nulls, add recursive incase name spaces
 | 
			
		||||
    pm.delete(pm.ls('*' + kNullSuffix + '*', recursive=True))
 | 
			
		||||
 | 
			
		||||
    # get all capsules in scene
 | 
			
		||||
    capsule_list = getCapsule(True) if springMagic.is_collision else None
 | 
			
		||||
 | 
			
		||||
    if progression_callback:
 | 
			
		||||
        progression_callback(0)
 | 
			
		||||
 | 
			
		||||
    # Save object previous frame information in a ordered dict
 | 
			
		||||
    spring_data_dict = OrderedDict()
 | 
			
		||||
 | 
			
		||||
    # Initialize data on the first frame
 | 
			
		||||
    pm.currentTime(start_frame, edit=True)
 | 
			
		||||
 | 
			
		||||
    # Create a list of objects chains
 | 
			
		||||
    # ie [[nt.Joint(u'joint1'), nt.Joint(u'joint2'), nt.Joint(u'joint4')], [nt.Joint(u'joint7'), nt.Joint(u'joint8'), nt.Joint(u'joint10')]]
 | 
			
		||||
    all_joints_descendants_list = pm.listRelatives(objs, allDescendents=True, type='transform')
 | 
			
		||||
    top_hierarchy_joints_list = [x for x in objs if x not in all_joints_descendants_list]
 | 
			
		||||
 | 
			
		||||
    # transforms_chains_list = map(lambda x: [x] + [y for y in pm.listRelatives(x, allDescendents=True) if y in objs][::-1], top_hierarchy_joints_list)
 | 
			
		||||
 | 
			
		||||
    # Deal with the specific case of root bone with no parent.
 | 
			
		||||
    # The root bone is considered the driver, so we remove it from the calculation.
 | 
			
		||||
    transforms_chains_list = map(lambda x: ([x] if x.getParent() else []) + [y for y in pm.listRelatives(x, allDescendents=True) if y in objs][::-1], top_hierarchy_joints_list)
 | 
			
		||||
 | 
			
		||||
    # Remove empty lists
 | 
			
		||||
    transforms_chains_list = [x for x in transforms_chains_list if x != []]
 | 
			
		||||
 | 
			
		||||
    # Create progression bar generator values
 | 
			
		||||
    number_of_progession_step = 0
 | 
			
		||||
 | 
			
		||||
    if springMagic.is_pose_match:
 | 
			
		||||
        number_of_progession_step += end_frame - start_frame + 1
 | 
			
		||||
 | 
			
		||||
    if springMagic.is_loop:
 | 
			
		||||
        # Doesn't process the first frame on the first loop
 | 
			
		||||
        number_of_progession_step += ((end_frame - start_frame) * 2 + 1) * sub_div
 | 
			
		||||
    else:
 | 
			
		||||
        # Doesn't process the first frame
 | 
			
		||||
        number_of_progession_step += (end_frame - start_frame) * sub_div
 | 
			
		||||
 | 
			
		||||
    progression_increment = 100.0 / number_of_progession_step
 | 
			
		||||
    progression_generator = frange(progression_increment, 100.0 + progression_increment, progression_increment)
 | 
			
		||||
 | 
			
		||||
    # Create spring data for each transforms at start frame
 | 
			
		||||
    for transforms_chain in transforms_chains_list:
 | 
			
		||||
 | 
			
		||||
        if SpringMagicMaya.isInterrupted():
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
        transforms_cycle = cycle(transforms_chain)
 | 
			
		||||
 | 
			
		||||
        # Prime the pump
 | 
			
		||||
        parent = first_transform = next(transforms_cycle)
 | 
			
		||||
        grand_parent = parent.getParent()
 | 
			
		||||
        child = next(transforms_cycle)
 | 
			
		||||
        grand_child = next(transforms_cycle)
 | 
			
		||||
 | 
			
		||||
        # skip end bone
 | 
			
		||||
        for transform in transforms_chain[:-1]:
 | 
			
		||||
 | 
			
		||||
            if SpringMagicMaya.isInterrupted():
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            # End of cycle iteration
 | 
			
		||||
            if grand_child == first_transform:
 | 
			
		||||
                grand_child = None
 | 
			
		||||
 | 
			
		||||
            spring_data_dict[parent.name()] = SpringData(springMagic, spring, parent, child, grand_child, grand_parent)
 | 
			
		||||
 | 
			
		||||
            grand_parent, parent, child, grand_child = parent, child, grand_child, next(transforms_cycle)
 | 
			
		||||
 | 
			
		||||
    # Save joints position over timeline
 | 
			
		||||
    # Parse timeline just one time
 | 
			
		||||
    if springMagic.is_pose_match:
 | 
			
		||||
        for frame in range(0, end_frame - start_frame + 1):
 | 
			
		||||
 | 
			
		||||
            if SpringMagicMaya.isInterrupted():
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            pm.currentTime(start_frame + frame, edit=True)
 | 
			
		||||
 | 
			
		||||
            for spring_data in spring_data_dict.values():
 | 
			
		||||
 | 
			
		||||
                if not SpringMagicMaya.isInterrupted():
 | 
			
		||||
                    spring_data.keyframe_child_proxy()
 | 
			
		||||
 | 
			
		||||
            progression = progression_generator.next()
 | 
			
		||||
            progression = clamp(progression, 0, 100)
 | 
			
		||||
 | 
			
		||||
            if progression_callback:
 | 
			
		||||
                progression_callback(progression)
 | 
			
		||||
 | 
			
		||||
            SpringMagicMaya.progress(progression)
 | 
			
		||||
 | 
			
		||||
    # Generate frame index
 | 
			
		||||
    # Skip first frame on first calculation pass
 | 
			
		||||
    frame_increment = 1.0 / sub_div
 | 
			
		||||
    frame_generator = frange(frame_increment, end_frame - start_frame + frame_increment, frame_increment)
 | 
			
		||||
 | 
			
		||||
    # On second calculation pass compute first frame
 | 
			
		||||
    if springMagic.is_loop:
 | 
			
		||||
        frame_generator = chain(frame_generator, frange(0, end_frame - start_frame + frame_increment, frame_increment))
 | 
			
		||||
 | 
			
		||||
    for frame in frame_generator:
 | 
			
		||||
 | 
			
		||||
        # print('Frame: ' + str(frame))
 | 
			
		||||
 | 
			
		||||
        if SpringMagicMaya.isInterrupted():
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
        pm.currentTime(start_frame + frame, edit=True)
 | 
			
		||||
 | 
			
		||||
        for previous_frame_spring_data in spring_data_dict.values():
 | 
			
		||||
 | 
			
		||||
            if SpringMagicMaya.isInterrupted():
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            grand_parent_spring_data = None
 | 
			
		||||
            if previous_frame_spring_data.grand_parent and previous_frame_spring_data.grand_parent.name() in spring_data_dict.keys():
 | 
			
		||||
                grand_parent_spring_data = spring_data_dict[previous_frame_spring_data.grand_parent.name()]
 | 
			
		||||
 | 
			
		||||
            # get current position of parent and child
 | 
			
		||||
            parent_pos = get_translation(previous_frame_spring_data.parent)
 | 
			
		||||
 | 
			
		||||
            # print("obj: " + str(previous_frame_spring_data.parent.name()))
 | 
			
		||||
 | 
			
		||||
            new_child_pos = get_translation(previous_frame_spring_data.child_proxy)
 | 
			
		||||
 | 
			
		||||
            # Apply inertia
 | 
			
		||||
            new_child_pos += previous_frame_spring_data.apply_inertia(new_child_pos)
 | 
			
		||||
 | 
			
		||||
            # apply wind
 | 
			
		||||
            new_child_pos += previous_frame_spring_data.apply_wind(start_frame + frame)
 | 
			
		||||
 | 
			
		||||
            # detect collision
 | 
			
		||||
            has_collision, new_child_pos, child_pos_corrected = previous_frame_spring_data.detect_collision(parent_pos, new_child_pos, capsule_list)
 | 
			
		||||
 | 
			
		||||
            # detect plane collision
 | 
			
		||||
            grand_parent_has_plane_collision = False
 | 
			
		||||
            if grand_parent_spring_data:
 | 
			
		||||
                grand_parent_has_plane_collision = grand_parent_spring_data.has_plane_collide
 | 
			
		||||
 | 
			
		||||
            has_hit_plane, new_child_pos = previous_frame_spring_data.detect_plane_hit(parent_pos, new_child_pos, grand_parent_has_plane_collision)
 | 
			
		||||
 | 
			
		||||
            # calculate upvector by interpolation y axis for twist
 | 
			
		||||
            up_vector = previous_frame_spring_data.compute_up_vector()
 | 
			
		||||
 | 
			
		||||
            # apply aim constraint to do actual rotation
 | 
			
		||||
            previous_frame_spring_data.aim_by_ratio(up_vector, new_child_pos, child_pos_corrected)
 | 
			
		||||
 | 
			
		||||
            # Extend bone if needed (update child translation)
 | 
			
		||||
            previous_frame_spring_data.extend_bone(child_pos_corrected)
 | 
			
		||||
 | 
			
		||||
            # Update current transform with the new values
 | 
			
		||||
            previous_frame_spring_data.update(has_collision, has_hit_plane, child_pos_corrected)
 | 
			
		||||
 | 
			
		||||
            # Update the grand parent has_child_collide value
 | 
			
		||||
            if grand_parent_spring_data:
 | 
			
		||||
                grand_parent_spring_data.has_child_collide = has_collision
 | 
			
		||||
 | 
			
		||||
        progression = progression_generator.next()
 | 
			
		||||
        progression = clamp(progression, 0, 100)
 | 
			
		||||
 | 
			
		||||
        if progression_callback:
 | 
			
		||||
            progression_callback(progression)
 | 
			
		||||
 | 
			
		||||
        SpringMagicMaya.progress(progression)
 | 
			
		||||
 | 
			
		||||
    # bake result on frame
 | 
			
		||||
    if springMagic.wipe_subframe and not SpringMagicMaya.isInterrupted():
 | 
			
		||||
        transform_to_bake_list = [spring_data.parent for spring_data in spring_data_dict.values()]
 | 
			
		||||
 | 
			
		||||
        # Deactivate all pairblend otherwise bake doesn't work with animation layers
 | 
			
		||||
        for spring_data in spring_data_dict.values():
 | 
			
		||||
            spring_data.set_pairblend_weight(0.0)
 | 
			
		||||
 | 
			
		||||
        bakeAnim(transform_to_bake_list, start_frame, end_frame)
 | 
			
		||||
 | 
			
		||||
def bakeAnim(objList, startFrame, endFrame):
 | 
			
		||||
    pm.bakeResults(
 | 
			
		||||
        objList,
 | 
			
		||||
        t=(startFrame, endFrame),
 | 
			
		||||
        sampleBy=1,
 | 
			
		||||
        disableImplicitControl=False,
 | 
			
		||||
        preserveOutsideKeys=True,
 | 
			
		||||
        sparseAnimCurveBake=False,
 | 
			
		||||
        removeBakedAttributeFromLayer=False,
 | 
			
		||||
        bakeOnOverrideLayer=False,
 | 
			
		||||
        minimizeRotation=True,
 | 
			
		||||
        shape=False,
 | 
			
		||||
        simulation=False)
 | 
			
		||||
 | 
			
		||||
SM_boneTransformDict = {}
 | 
			
		||||
 | 
			
		||||
def copyBonePose():
 | 
			
		||||
    global SM_boneTransformDict
 | 
			
		||||
 | 
			
		||||
    for obj in pm.ls(sl=True):
 | 
			
		||||
        SM_boneTransformDict[obj] = [obj.getTranslation(), obj.getRotation()]
 | 
			
		||||
 | 
			
		||||
def pasteBonePose():
 | 
			
		||||
    global SM_boneTransformDict
 | 
			
		||||
 | 
			
		||||
    for obj in pm.ls(sl=True):
 | 
			
		||||
        if obj in SM_boneTransformDict.keys():
 | 
			
		||||
 | 
			
		||||
            logging.debug(SM_boneTransformDict[obj][0])
 | 
			
		||||
 | 
			
		||||
            obj.setTranslation(SM_boneTransformDict[obj][0])
 | 
			
		||||
            obj.setRotation(SM_boneTransformDict[obj][1])
 | 
			
		||||
 | 
			
		||||
def preCheckCollision(objPos, objLength, capsuleList):
 | 
			
		||||
 | 
			
		||||
    # print('objPos:' + str(objPos))
 | 
			
		||||
    # print('objLength:' + str(objLength))
 | 
			
		||||
 | 
			
		||||
    # pre check bone length compare with collision body radius
 | 
			
		||||
    # will improve performance if bone is far from capsule
 | 
			
		||||
    for capsule in capsuleList:
 | 
			
		||||
        capsule_children_list = pm.listRelatives(capsule, children=1, type='transform')
 | 
			
		||||
 | 
			
		||||
        p = capsule_children_list[0].getTranslation(space='world')
 | 
			
		||||
        q = capsule_children_list[1].getTranslation(space='world')
 | 
			
		||||
        r = capsule.getAttr('scaleZ')
 | 
			
		||||
 | 
			
		||||
        bone_to_capsule_distance = springMath.dist_to_line(p, q, objPos)
 | 
			
		||||
 | 
			
		||||
        # print('p:' + str(p))
 | 
			
		||||
        # print('q:' + str(q))
 | 
			
		||||
        # print('r:' + str(r))
 | 
			
		||||
        # print('boneToCapsuleDistance:' + str(bone_to_capsule_distance))
 | 
			
		||||
 | 
			
		||||
        # means close enough to have a hit change
 | 
			
		||||
        if bone_to_capsule_distance < objLength + r:
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
def repeatMoveToPlane(obj, objPos, objTarget, colPlane, times):
 | 
			
		||||
    # Y axis direction of plane
 | 
			
		||||
    n = dt.Vector(get_matrix(colPlane)[4:7])
 | 
			
		||||
    q = get_translation(colPlane)
 | 
			
		||||
    d = n.dot(q)
 | 
			
		||||
 | 
			
		||||
    # for i in range(times):
 | 
			
		||||
    #     pt = objPos
 | 
			
		||||
    #     obj.setTranslation(proj_pt_to_plane(pt, n, d), space='world')
 | 
			
		||||
    #     if (i + 1) != times:
 | 
			
		||||
    #         obj.setTranslation(get_translation(objTarget), space='world')
 | 
			
		||||
    pt = objPos
 | 
			
		||||
    outPos = springMath.proj_pt_to_plane(pt, n, d)
 | 
			
		||||
 | 
			
		||||
    return outPos
 | 
			
		||||
 | 
			
		||||
def setWireShading(obj, tmp):
 | 
			
		||||
    obj.getShape().overrideEnabled.set(True)
 | 
			
		||||
    obj.getShape().overrideShading.set(False)
 | 
			
		||||
 | 
			
		||||
    if tmp:
 | 
			
		||||
        obj.getShape().overrideDisplayType.set(1)
 | 
			
		||||
 | 
			
		||||
def addCapsuleSphereConstraint(sphereObj):
 | 
			
		||||
    # create a locator and make sphere follow it
 | 
			
		||||
    locator = pm.spaceLocator(name=sphereObj.name() + '_locator' + kCapsuleNameSuffix)
 | 
			
		||||
 | 
			
		||||
    locator.setTranslation(sphereObj.getTranslation())
 | 
			
		||||
    locator.setRotation(sphereObj.getRotation())
 | 
			
		||||
    locator.getShape().setAttr('visibility', False)
 | 
			
		||||
 | 
			
		||||
    pm.parentConstraint(locator, sphereObj)
 | 
			
		||||
 | 
			
		||||
    return locator
 | 
			
		||||
 | 
			
		||||
def createCapsuleGeometry(size):
 | 
			
		||||
    # create geometry
 | 
			
		||||
    cylinder, cylinder_history = pm.cylinder(radius=size, sections=8, heightRatio=3)
 | 
			
		||||
    pm.rename(cylinder.name(), cylinder.name() + kCapsuleNameSuffix)
 | 
			
		||||
 | 
			
		||||
    sphereA, sphereA_history = pm.sphere(radius=size, endSweep=180, sections=4)
 | 
			
		||||
    pm.rename(sphereA.name(), sphereA.name() + kCapsuleNameSuffix)
 | 
			
		||||
 | 
			
		||||
    sphereB, sphereB_history = pm.sphere(radius=size, endSweep=180, sections=4)
 | 
			
		||||
    pm.rename(sphereB.name(), sphereB.name() + kCapsuleNameSuffix)
 | 
			
		||||
 | 
			
		||||
    # set to wireframe shader
 | 
			
		||||
    setWireShading(cylinder, False)
 | 
			
		||||
    setWireShading(sphereA, True)
 | 
			
		||||
    setWireShading(sphereB, True)
 | 
			
		||||
 | 
			
		||||
    # build a capsule with geometry
 | 
			
		||||
    cylinder.setAttr('rotateZ', 90)
 | 
			
		||||
    sphereA.setAttr('translateY', -1.5 * size)
 | 
			
		||||
    sphereB.setAttr('rotateZ', 180)
 | 
			
		||||
    sphereB.setAttr('translateY', 1.5 * size)
 | 
			
		||||
 | 
			
		||||
    # add constrain
 | 
			
		||||
    locatorA = addCapsuleSphereConstraint(sphereA)
 | 
			
		||||
    locatorB = addCapsuleSphereConstraint(sphereB)
 | 
			
		||||
 | 
			
		||||
    pm.parent(locatorA, cylinder)
 | 
			
		||||
    pm.parent(locatorB, cylinder)
 | 
			
		||||
 | 
			
		||||
    pm.parent(sphereA, cylinder)
 | 
			
		||||
    pm.parent(sphereB, cylinder)
 | 
			
		||||
 | 
			
		||||
    sphereA.setAttr('inheritsTransform', False)
 | 
			
		||||
    sphereB.setAttr('inheritsTransform', False)
 | 
			
		||||
 | 
			
		||||
    pm.connectAttr(cylinder.scaleY, (sphereA_history.name() + '.radius'))
 | 
			
		||||
    pm.connectAttr(cylinder.scaleY, (sphereB_history.name() + '.radius'))
 | 
			
		||||
    pm.connectAttr(cylinder.scaleY, cylinder.scaleZ)
 | 
			
		||||
 | 
			
		||||
    return cylinder
 | 
			
		||||
 | 
			
		||||
def getCapsule(getAll):
 | 
			
		||||
    if getAll:
 | 
			
		||||
        nurbsTransLst = pm.ls(type='transform')
 | 
			
		||||
    else:
 | 
			
		||||
        nurbsTransLst = pm.ls(sl=True)
 | 
			
		||||
 | 
			
		||||
    nurbsSurfaceLst = []
 | 
			
		||||
    for obj in nurbsTransLst:
 | 
			
		||||
        if obj.getShape() and (pm.nodeType(obj.getShape()) == 'nurbsSurface'):
 | 
			
		||||
            nurbsSurfaceLst.append(obj)
 | 
			
		||||
 | 
			
		||||
    cylinderLst = []
 | 
			
		||||
    for obj in nurbsTransLst:
 | 
			
		||||
        if 'ylinder' in obj.name() and kCapsuleNameSuffix in obj.name():
 | 
			
		||||
            cylinderLst.append(obj)
 | 
			
		||||
 | 
			
		||||
    return cylinderLst
 | 
			
		||||
 | 
			
		||||
def addCapsuleBody():
 | 
			
		||||
    # create capsule body for collision
 | 
			
		||||
    # place capsule at ori point of nothing selected in scene
 | 
			
		||||
    # place capsule match with object position and rotation if select scene object
 | 
			
		||||
    collisionBoneList = []
 | 
			
		||||
    objs = pm.ls(sl=True)
 | 
			
		||||
 | 
			
		||||
    for obj in objs:
 | 
			
		||||
        children = pm.listRelatives(obj, children=1)
 | 
			
		||||
 | 
			
		||||
        # only add capsule to the obj which has child
 | 
			
		||||
        if children:
 | 
			
		||||
            collisionBoneList.append([obj, children[0]])
 | 
			
		||||
 | 
			
		||||
    if collisionBoneList:
 | 
			
		||||
        for couple in collisionBoneList:
 | 
			
		||||
            baseBone = couple[0]
 | 
			
		||||
            endBone = couple[1]
 | 
			
		||||
            capsule = createCapsuleGeometry(1)
 | 
			
		||||
 | 
			
		||||
            pm.parent(capsule, baseBone)
 | 
			
		||||
            # match capsule to bone
 | 
			
		||||
            endBoneTrans = endBone.getTranslation()
 | 
			
		||||
            capsule.setTranslation(endBoneTrans * 0.5)
 | 
			
		||||
            capsule.setAttr('scaleX', endBoneTrans[0] / 3)
 | 
			
		||||
            capsule.setAttr('scaleY', endBoneTrans[0] / 3)
 | 
			
		||||
            cns = pm.aimConstraint(endBone, capsule, aimVector=[1, 0, 0])
 | 
			
		||||
            pm.delete(cns)
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        capsule = createCapsuleGeometry(1)
 | 
			
		||||
        capsule.setAttr('scaleX', 10)
 | 
			
		||||
        capsule.setAttr('scaleY', 10)
 | 
			
		||||
        pm.select(clear=1)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										119
									
								
								Scripts/Animation/springmagic/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,119 @@
 | 
			
		||||
import maya.mel as mel
 | 
			
		||||
import maya.cmds as cmds
 | 
			
		||||
 | 
			
		||||
from functools import wraps
 | 
			
		||||
 | 
			
		||||
# -----------------------------------------------------------------------------
 | 
			
		||||
# Decorators
 | 
			
		||||
# -----------------------------------------------------------------------------
 | 
			
		||||
def viewportOff(func):
 | 
			
		||||
    """
 | 
			
		||||
    Decorator - turn off Maya display while func is running.
 | 
			
		||||
    if func will fail, the error will be raised after.
 | 
			
		||||
    """
 | 
			
		||||
    @wraps(func)
 | 
			
		||||
    def wrap(*args, **kwargs):
 | 
			
		||||
 | 
			
		||||
        # Turn $gMainPane Off:
 | 
			
		||||
        mel.eval("paneLayout -e -manage false $gMainPane")
 | 
			
		||||
 | 
			
		||||
        # Decorator will try/except running the function.
 | 
			
		||||
        # But it will always turn on the viewport at the end.
 | 
			
		||||
        # In case the function failed, it will prevent leaving maya viewport off.
 | 
			
		||||
        try:
 | 
			
		||||
            return func(*args, **kwargs)
 | 
			
		||||
        except Exception:
 | 
			
		||||
            raise  # will raise original error
 | 
			
		||||
        finally:
 | 
			
		||||
            mel.eval("paneLayout -e -manage true $gMainPane")
 | 
			
		||||
 | 
			
		||||
    return wrap
 | 
			
		||||
 | 
			
		||||
class gShowProgress(object):
 | 
			
		||||
    """
 | 
			
		||||
    Function decorator to show the user (progress) feedback.
 | 
			
		||||
    @usage
 | 
			
		||||
 | 
			
		||||
    import time
 | 
			
		||||
    @gShowProgress(end=10)
 | 
			
		||||
    def createCubes():
 | 
			
		||||
        for i in range(10):
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
            if createCubes.isInterrupted(): break
 | 
			
		||||
            iCube = cmds.polyCube(w=1,h=1,d=1)
 | 
			
		||||
            cmds.move(i,i*.2,0,iCube)
 | 
			
		||||
            createCubes.step()
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, status='Busy...', start=0, end=100, interruptable=True):
 | 
			
		||||
        import maya.mel
 | 
			
		||||
 | 
			
		||||
        self.mStartValue = start
 | 
			
		||||
        self.mEndValue = end
 | 
			
		||||
        self.mStatus = status
 | 
			
		||||
        self.mInterruptable = interruptable
 | 
			
		||||
        self.mMainProgressBar = maya.mel.eval('$tmp = $gMainProgressBar')
 | 
			
		||||
 | 
			
		||||
    def step(self, inValue=1):
 | 
			
		||||
        """Increase step
 | 
			
		||||
        @param inValue (int) Step value"""
 | 
			
		||||
        cmds.progressBar(self.mMainProgressBar, edit=True, step=inValue)
 | 
			
		||||
 | 
			
		||||
    def progress(self, inValue):
 | 
			
		||||
        """Set progression value
 | 
			
		||||
        @param inValue (int) Progress value"""
 | 
			
		||||
        cmds.progressBar(self.mMainProgressBar, edit=True, progress=inValue)
 | 
			
		||||
 | 
			
		||||
    def isInterrupted(self):
 | 
			
		||||
        """Check if the user has interrupted the progress
 | 
			
		||||
        @return (boolean)"""
 | 
			
		||||
        return cmds.progressBar(self.mMainProgressBar, query=True, isCancelled=True)
 | 
			
		||||
 | 
			
		||||
    def start(self):
 | 
			
		||||
        """Start progress"""
 | 
			
		||||
        cmds.waitCursor(state=True)
 | 
			
		||||
        cmds.progressBar(
 | 
			
		||||
            self.mMainProgressBar,
 | 
			
		||||
            edit=True,
 | 
			
		||||
            beginProgress=True,
 | 
			
		||||
            isInterruptable=self.mInterruptable,
 | 
			
		||||
            status=self.mStatus,
 | 
			
		||||
            minValue=self.mStartValue,
 | 
			
		||||
            maxValue=self.mEndValue)
 | 
			
		||||
        cmds.refresh()
 | 
			
		||||
 | 
			
		||||
    def end(self):
 | 
			
		||||
        """Mark the progress as ended"""
 | 
			
		||||
        cmds.progressBar(self.mMainProgressBar, edit=True, endProgress=True)
 | 
			
		||||
        cmds.waitCursor(state=False)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, inFunction):
 | 
			
		||||
        """
 | 
			
		||||
        Override call method
 | 
			
		||||
        @param inFunction (function) Original function
 | 
			
		||||
        @return (function) Wrapped function
 | 
			
		||||
        @description
 | 
			
		||||
            If there are decorator arguments, __call__() is only called once,
 | 
			
		||||
            as part of the decoration process! You can only give it a single argument,
 | 
			
		||||
            which is the function object.
 | 
			
		||||
        """
 | 
			
		||||
        def wrapped_f(*args, **kwargs):
 | 
			
		||||
            # Start progress
 | 
			
		||||
            self.start()
 | 
			
		||||
            # Call original function
 | 
			
		||||
            inFunction(*args, **kwargs)
 | 
			
		||||
            # End progress
 | 
			
		||||
            self.end()
 | 
			
		||||
 | 
			
		||||
        # Add special methods to the wrapped function
 | 
			
		||||
        wrapped_f.step = self.step
 | 
			
		||||
        wrapped_f.progress = self.progress
 | 
			
		||||
        wrapped_f.isInterrupted = self.isInterrupted
 | 
			
		||||
 | 
			
		||||
        # Copy over attributes
 | 
			
		||||
        wrapped_f.__doc__ = inFunction.__doc__
 | 
			
		||||
        wrapped_f.__name__ = inFunction.__name__
 | 
			
		||||
        wrapped_f.__module__ = inFunction.__module__
 | 
			
		||||
 | 
			
		||||
        # Return wrapped function
 | 
			
		||||
        return wrapped_f
 | 
			
		||||
							
								
								
									
										75
									
								
								Scripts/Animation/springmagic/history.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,75 @@
 | 
			
		||||
#####################################################################################
 | 
			
		||||
#
 | 
			
		||||
# Spring Magic for Maya
 | 
			
		||||
#
 | 
			
		||||
# Calculate bone chain animation by settings, support collisions and wind force
 | 
			
		||||
# Can work with rigging controller as well
 | 
			
		||||
#
 | 
			
		||||
# Need pringMagic.ui file to work with
 | 
			
		||||
# This script need also icon file support, which should be put in same folder
 | 
			
		||||
#
 | 
			
		||||
# feel free to mail me redtank@outlook.com for any bug or issue
 | 
			
		||||
#
 | 
			
		||||
# Yanbin Bai
 | 
			
		||||
# 2021.02
 | 
			
		||||
#
 | 
			
		||||
#####################################################################################
 | 
			
		||||
 | 
			
		||||
3.5a
 | 
			
		||||
- Fix bug tension calculation introduced in the 3.5 (Benoit Degand)
 | 
			
		||||
- Fix bug inertia calculation introduced in the 3.5 (Benoit Degand)
 | 
			
		||||
- Clarify code splitting source code in class and methods (Benoit Degand)
 | 
			
		||||
 | 
			
		||||
3.5
 | 
			
		||||
- Apply PEP8 coding format (Benoit Degand)
 | 
			
		||||
- Add possiblity to cancel the operation (Esc) (Benoit Degand)
 | 
			
		||||
- Increase speed (x2), avoiding locators and aim constraints intensive creation/deletion (Benoit Degand)
 | 
			
		||||
- Fragment source code in several files (Benoit Degand)
 | 
			
		||||
- Pose Match default off
 | 
			
		||||
 | 
			
		||||
3.4b
 | 
			
		||||
- fix collision bug
 | 
			
		||||
 | 
			
		||||
3.4a
 | 
			
		||||
- fix wind bug
 | 
			
		||||
 | 
			
		||||
3.4
 | 
			
		||||
- add plane collision
 | 
			
		||||
- add pose match
 | 
			
		||||
 | 
			
		||||
3.3
 | 
			
		||||
- add inertia effect
 | 
			
		||||
 | 
			
		||||
3.2
 | 
			
		||||
- fix wind effect cannot set key issue
 | 
			
		||||
 | 
			
		||||
3.1
 | 
			
		||||
- add bind controller
 | 
			
		||||
- add wind
 | 
			
		||||
- add flex setting
 | 
			
		||||
- improve performance
 | 
			
		||||
- fix twist bug
 | 
			
		||||
- add capsule icon
 | 
			
		||||
- seperate skinTools to spring magic and skin magic
 | 
			
		||||
 | 
			
		||||
3.0
 | 
			
		||||
- re-write spring magic to improve performance
 | 
			
		||||
- add capsule collision for spring magic
 | 
			
		||||
- add donate page
 | 
			
		||||
 | 
			
		||||
2.7.8
 | 
			
		||||
- fix script stop working issue cause by highend3d.com changed their web page
 | 
			
		||||
 | 
			
		||||
2.7.7
 | 
			
		||||
- add time out for update checking in case of network issue
 | 
			
		||||
 | 
			
		||||
2.7.6
 | 
			
		||||
- fix spring magic calculation issue on MAYA 2016
 | 
			
		||||
- update UI for MAYA 2016
 | 
			
		||||
Thanks for all the help from Nobuyuki Kobayashi nobuyuki@unity3d.com
 | 
			
		||||
 | 
			
		||||
2.7.5
 | 
			
		||||
- add floor collision to spring magic
 | 
			
		||||
 | 
			
		||||
2.7
 | 
			
		||||
- Add spring magic
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/China Flag.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/Shelf.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/Title.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/addCapsule.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/addPlane.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/ali_pay.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 31 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/bilibili.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 22 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/bitcoin.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/clearCapsule.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/ctrl_bake.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/ctrl_bind.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.0 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/donut.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/english.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/info.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/japanese.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/language.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/linkedin.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 5.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/paypal.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/redo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/removeCapsule.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/spring.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/update.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/vimeo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/wechat_pay.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 29 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/weightBone.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/wind.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Scripts/Animation/springmagic/icons/youtube.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 18 KiB  | 
							
								
								
									
										12
									
								
								Scripts/Animation/springmagic/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,12 @@
 | 
			
		||||
import Animation.springmagic as springmagic
 | 
			
		||||
import Animation.springmagic.ui as ui
 | 
			
		||||
 | 
			
		||||
def main(*args, **kwargs):
 | 
			
		||||
 | 
			
		||||
    widget = ui.SpringMagicWidget()
 | 
			
		||||
    widget.show()
 | 
			
		||||
 | 
			
		||||
# if __name__ == "__main__":
 | 
			
		||||
 | 
			
		||||
#     with springmagic.app():
 | 
			
		||||
#         springmagic.main()
 | 
			
		||||
							
								
								
									
										34
									
								
								Scripts/Animation/springmagic/mkDevTools.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,34 @@
 | 
			
		||||
from os import path
 | 
			
		||||
import compileall
 | 
			
		||||
import sys, types
 | 
			
		||||
 | 
			
		||||
def recompile(modulename):
 | 
			
		||||
    """Recompile the given module, its directory's contents!"""
 | 
			
		||||
    myScriptPath = sys.modules[modulename.__name__].__path__[0]
 | 
			
		||||
    if path.isdir(myScriptPath):
 | 
			
		||||
        compileall.compile_dir(myScriptPath, force=True)
 | 
			
		||||
 | 
			
		||||
def reload_module(modulename):
 | 
			
		||||
    """Reload the given module and all children"""
 | 
			
		||||
 
 | 
			
		||||
    # Get a reference to each loaded module
 | 
			
		||||
    loaded_modules = dict([(key, value) for key, value in sys.modules.items() 
 | 
			
		||||
        if key.startswith(modulename.__name__) and 
 | 
			
		||||
        isinstance(value, types.ModuleType)])
 | 
			
		||||
 
 | 
			
		||||
    # Delete references to these loaded modules from sys.modules
 | 
			
		||||
    for key in loaded_modules:
 | 
			
		||||
        del sys.modules[key]
 | 
			
		||||
 
 | 
			
		||||
    # Load each of the modules again
 | 
			
		||||
    # Make old modules share state with new modules
 | 
			
		||||
    for key in loaded_modules:
 | 
			
		||||
        print 're-loading %s' % key
 | 
			
		||||
        newmodule = __import__(key)
 | 
			
		||||
        oldmodule = loaded_modules[key]
 | 
			
		||||
        oldmodule.__dict__.clear()
 | 
			
		||||
        oldmodule.__dict__.update(newmodule.__dict__)
 | 
			
		||||
 | 
			
		||||
def refresh(modulename):
 | 
			
		||||
    recompile(modulename)
 | 
			
		||||
    reload_module(modulename)
 | 
			
		||||
							
								
								
									
										27
									
								
								Scripts/Animation/springmagic/springMagic.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,27 @@
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import inspect
 | 
			
		||||
 | 
			
		||||
# Add SprinMagic path to PYTHON_PATH
 | 
			
		||||
script_name = inspect.getframeinfo(inspect.currentframe()).filename
 | 
			
		||||
script_path = os.path.dirname(os.path.abspath(script_name))
 | 
			
		||||
path_name = os.path.dirname(script_path)
 | 
			
		||||
 | 
			
		||||
if os.path.exists(path_name) and path_name not in sys.path:
 | 
			
		||||
    sys.path.append(path_name)
 | 
			
		||||
 | 
			
		||||
# Import SpringMagic module
 | 
			
		||||
import springmagic
 | 
			
		||||
import springmagic.mkDevTools as dev
 | 
			
		||||
 | 
			
		||||
# Recompile SpringMagic module if modification has been made
 | 
			
		||||
# dev.refresh(springmagic)
 | 
			
		||||
 | 
			
		||||
# Launch SpringMagic
 | 
			
		||||
springmagic.main()
 | 
			
		||||
 | 
			
		||||
# Unload SpringMagic module
 | 
			
		||||
del(springmagic)
 | 
			
		||||
 | 
			
		||||
# Remove SprinMagic path from PYTHON_PATH
 | 
			
		||||
sys.path.remove(path_name)
 | 
			
		||||
							
								
								
									
										1931
									
								
								Scripts/Animation/springmagic/springMagic.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2085
									
								
								Scripts/Animation/springmagic/springMagic_chn.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1931
									
								
								Scripts/Animation/springmagic/springMagic_eng.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										2028
									
								
								Scripts/Animation/springmagic/springMagic_jpn.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										274
									
								
								Scripts/Animation/springmagic/springMath.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,274 @@
 | 
			
		||||
import math
 | 
			
		||||
import pymel.core as pm
 | 
			
		||||
import pymel.core.datatypes as dt
 | 
			
		||||
 | 
			
		||||
def sigmoid(x):
 | 
			
		||||
    return 1 / (1 + math.exp(-x))
 | 
			
		||||
 | 
			
		||||
def distance(a, b):
 | 
			
		||||
    return (b - a).length()
 | 
			
		||||
 | 
			
		||||
def lerp_vec(a, b, t):
 | 
			
		||||
    return (a * (1 - t)) + (b * t)
 | 
			
		||||
 | 
			
		||||
def dist_to_plane(pt, n, d):
 | 
			
		||||
    return n.dot(pt) - (d / n.dot(n))
 | 
			
		||||
 | 
			
		||||
def dist_to_line(a, b, p):
 | 
			
		||||
    ap = p - a
 | 
			
		||||
    ab = b - a
 | 
			
		||||
    result = a + ((ap.dot(ab) / ab.dot(ab)) * ab)
 | 
			
		||||
    return distance(result, p)
 | 
			
		||||
 | 
			
		||||
def is_same_side_of_plane(pt, test_pt, n, d):
 | 
			
		||||
    d1 = math.copysign(1, dist_to_plane(pt, n, d))
 | 
			
		||||
    d2 = math.copysign(1, dist_to_plane(test_pt, n, d))
 | 
			
		||||
 | 
			
		||||
    # print(pt, test_pt, d1, d2)
 | 
			
		||||
    return d1 * d2 == 1.0
 | 
			
		||||
 | 
			
		||||
def proj_pt_to_plane(pt, n, d):
 | 
			
		||||
    t = n.dot(pt) - d
 | 
			
		||||
    return (pt - (n * t))
 | 
			
		||||
 | 
			
		||||
def pt_in_sphere(pt, c, r):
 | 
			
		||||
    return (pt - c).length() <= r
 | 
			
		||||
 | 
			
		||||
def pt_in_cylinder(pt, p, q, r):
 | 
			
		||||
    n = (q - p).normal()
 | 
			
		||||
    d = n.dot(p)
 | 
			
		||||
 | 
			
		||||
    if not is_same_side_of_plane(pt, (p + q) / 2.0, n, d):
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    n = (q - p).normal()
 | 
			
		||||
    d = n.dot(q)
 | 
			
		||||
 | 
			
		||||
    if not is_same_side_of_plane(pt, (p + q) / 2.0, n, d):
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    proj_pt = proj_pt_to_plane(pt, n, d)
 | 
			
		||||
    # logging("proj_pt", proj_pt)
 | 
			
		||||
    # logging("q", q)
 | 
			
		||||
    # logging("distance(proj_pt, q)", distance(proj_pt, q))
 | 
			
		||||
 | 
			
		||||
    return distance(proj_pt, q) <= r
 | 
			
		||||
 | 
			
		||||
def segment_sphere_isect(sa, sb, c, r):
 | 
			
		||||
    NotFound = (False, None)
 | 
			
		||||
 | 
			
		||||
    p = sa
 | 
			
		||||
    d = (sb - sa).normal()
 | 
			
		||||
 | 
			
		||||
    m = p - c
 | 
			
		||||
    b = m.dot(d)
 | 
			
		||||
    c = m.dot(m) - r * r
 | 
			
		||||
 | 
			
		||||
    if c > 0.0 and b > 0.0:
 | 
			
		||||
        return NotFound
 | 
			
		||||
 | 
			
		||||
    discr = b * b - c
 | 
			
		||||
    if discr < 0.0:
 | 
			
		||||
        return NotFound
 | 
			
		||||
 | 
			
		||||
    t = -b - math.sqrt(discr)
 | 
			
		||||
    if t < 0.0:
 | 
			
		||||
        return NotFound
 | 
			
		||||
 | 
			
		||||
    dist = distance(sa, sb)
 | 
			
		||||
    q = p + d * t
 | 
			
		||||
    return ((t >= 0 and t <= dist), q)
 | 
			
		||||
 | 
			
		||||
def segment_cylinder_isect(sa, sb, p, q, r):
 | 
			
		||||
    SM_EPSILON = 1e-6
 | 
			
		||||
    d = q - p
 | 
			
		||||
    m = sa - p
 | 
			
		||||
    n = sb - sa
 | 
			
		||||
    md = m.dot(d)
 | 
			
		||||
    nd = n.dot(d)
 | 
			
		||||
    dd = d.dot(d)
 | 
			
		||||
 | 
			
		||||
    NotFound = (False, None)
 | 
			
		||||
    if md < 0 and md + nd < 0:
 | 
			
		||||
        return NotFound
 | 
			
		||||
 | 
			
		||||
    if md > dd and md + nd > dd:
 | 
			
		||||
        return NotFound
 | 
			
		||||
 | 
			
		||||
    nn = n.dot(n)
 | 
			
		||||
    mn = m.dot(n)
 | 
			
		||||
 | 
			
		||||
    a = dd * nn - nd * nd
 | 
			
		||||
    k = m.dot(m) - r * r
 | 
			
		||||
    c = dd * k - md * md
 | 
			
		||||
 | 
			
		||||
    if abs(a) < SM_EPSILON:
 | 
			
		||||
        if c > 0:
 | 
			
		||||
            return NotFound
 | 
			
		||||
        if md < 0:
 | 
			
		||||
            t = -mn / nn
 | 
			
		||||
        elif md > dd:
 | 
			
		||||
            t = (nd - mn) / nn
 | 
			
		||||
        else:
 | 
			
		||||
            t = 0
 | 
			
		||||
        return (True, lerp_vec(sa, sb, t))
 | 
			
		||||
 | 
			
		||||
    b = dd * mn - nd * md
 | 
			
		||||
    discr = b * b - a * c
 | 
			
		||||
    if discr < 0:
 | 
			
		||||
        return NotFound
 | 
			
		||||
 | 
			
		||||
    t = (-b - math.sqrt(discr)) / a
 | 
			
		||||
    if t < 0.0 or t > 1.0:
 | 
			
		||||
        return NotFound
 | 
			
		||||
    if (md + t * nd < 0.0):
 | 
			
		||||
        if nd <= 0.0:
 | 
			
		||||
            return NotFound
 | 
			
		||||
        t = -md / nd
 | 
			
		||||
        return (k + 2 * t * (mn + t * nn) <= 0.0, lerp_vec(sa, sb, t))
 | 
			
		||||
    elif md + t * nd > dd:
 | 
			
		||||
        if nd >= 0.0:
 | 
			
		||||
            return NotFound
 | 
			
		||||
        t = (dd - md) / nd
 | 
			
		||||
        return (k + dd - 2 * md + t * (2 * (mn - nd) + t * nn) <= 0.0, lerp_vec(sa, sb, t))
 | 
			
		||||
 | 
			
		||||
    return (True, lerp_vec(sa, sb, t))
 | 
			
		||||
 | 
			
		||||
def pt_in_capsule(pt, p, q, r):
 | 
			
		||||
    return pt_in_cylinder(pt, p, q, r) or pt_in_sphere(pt, p, r) or pt_in_sphere(pt, q, r)
 | 
			
		||||
 | 
			
		||||
def segment_capsule_isect(sa, sb, p, q, r):
 | 
			
		||||
    # sa = dt.Vector()
 | 
			
		||||
    #    ray start point pos vector
 | 
			
		||||
    # sb = dt.Vector()
 | 
			
		||||
    #    ray end point pos vector
 | 
			
		||||
    # p = dt.Vector()
 | 
			
		||||
    #    capsle one sphere tip pos
 | 
			
		||||
    # q = dt.Vector()
 | 
			
		||||
    #    capsle another sphere tip pos
 | 
			
		||||
    # r = float
 | 
			
		||||
    #    radio of capsle sphere
 | 
			
		||||
 | 
			
		||||
    if pt_in_capsule(sa, p, q, r):
 | 
			
		||||
        if pt_in_capsule(sb, p, q, r):
 | 
			
		||||
            # both inside. extend sb to get intersection
 | 
			
		||||
            newb = sa + (sb - sa).normal() * 200.0
 | 
			
		||||
            sa, sb = newb, sa
 | 
			
		||||
        else:
 | 
			
		||||
            sb, sa = sa, sb
 | 
			
		||||
 | 
			
		||||
    # d = (sb - sa).normal()
 | 
			
		||||
 | 
			
		||||
    i1 = segment_sphere_isect(sa, sb, p, r)
 | 
			
		||||
    i2 = segment_sphere_isect(sa, sb, q, r)
 | 
			
		||||
    i3 = segment_cylinder_isect(sa, sb, p, q, r)
 | 
			
		||||
 | 
			
		||||
    dist = float('inf')
 | 
			
		||||
    closest_pt = None
 | 
			
		||||
    hit = False
 | 
			
		||||
    hitCylinder = False
 | 
			
		||||
 | 
			
		||||
    for i in [i1, i2, i3]:
 | 
			
		||||
 | 
			
		||||
        if i[0]:
 | 
			
		||||
            hit = True
 | 
			
		||||
            pt = i[1]
 | 
			
		||||
 | 
			
		||||
            if distance(sa, pt) < dist:
 | 
			
		||||
                closest_pt = pt
 | 
			
		||||
 | 
			
		||||
            dist = min(dist, distance(sa, pt))
 | 
			
		||||
            # draw_locator(i1[2], 'i1')
 | 
			
		||||
 | 
			
		||||
    return (hit, closest_pt, hitCylinder)
 | 
			
		||||
 | 
			
		||||
def checkCollision(cur_pos, pre_pos, capsuleLst, isRevert):
 | 
			
		||||
    # calculate collision with all the capsule in scene
 | 
			
		||||
    if isRevert:
 | 
			
		||||
        sa = cur_pos
 | 
			
		||||
        sb = pre_pos
 | 
			
		||||
    else:
 | 
			
		||||
        sb = cur_pos
 | 
			
		||||
        sa = pre_pos
 | 
			
		||||
 | 
			
		||||
    isHited = False
 | 
			
		||||
    closest_pt_dict = {}
 | 
			
		||||
 | 
			
		||||
    for obj in capsuleLst:
 | 
			
		||||
        objChildren = pm.listRelatives(obj, children=1, type='transform')
 | 
			
		||||
        p = objChildren[0].getTranslation(space='world')
 | 
			
		||||
        q = objChildren[1].getTranslation(space='world')
 | 
			
		||||
        r = obj.getAttr('scaleZ') * 1
 | 
			
		||||
 | 
			
		||||
        hit, closest_pt, hitCylinder = segment_capsule_isect(sa, sb, p, q, r)
 | 
			
		||||
 | 
			
		||||
        if hit:
 | 
			
		||||
            isHited = True
 | 
			
		||||
            closest_pt_dict[obj.name()] = [obj, closest_pt]
 | 
			
		||||
            # drawDebug_box(closest_pt)
 | 
			
		||||
 | 
			
		||||
    if isHited:
 | 
			
		||||
        pt_length = 9999
 | 
			
		||||
        closest_pt = None
 | 
			
		||||
        col_obj = None
 | 
			
		||||
 | 
			
		||||
        for pt in closest_pt_dict.keys():
 | 
			
		||||
            lLength = (closest_pt_dict[pt][1] - pre_pos).length()
 | 
			
		||||
 | 
			
		||||
            if lLength < pt_length:
 | 
			
		||||
                pt_length = lLength
 | 
			
		||||
                closest_pt = closest_pt_dict[pt][1]
 | 
			
		||||
                col_obj = closest_pt_dict[pt][0]
 | 
			
		||||
 | 
			
		||||
        # return col pt and col_body speed
 | 
			
		||||
        return closest_pt, col_obj, hitCylinder
 | 
			
		||||
    else:
 | 
			
		||||
        return None, None, None
 | 
			
		||||
 | 
			
		||||
def ckeckPointInTri(pos, pa, pb, pc):
 | 
			
		||||
    ra = math.acos(((pa - pos).normal()).dot((pb - pos).normal()))
 | 
			
		||||
    ra = dt.degrees(ra)
 | 
			
		||||
    rb = math.acos(((pb - pos).normal()).dot((pc - pos).normal()))
 | 
			
		||||
    rb = dt.degrees(rb)
 | 
			
		||||
    rc = math.acos(((pc - pos).normal()).dot((pa - pos).normal()))
 | 
			
		||||
    rc = dt.degrees(rc)
 | 
			
		||||
 | 
			
		||||
    return (abs(ra + rb + rc) > 359)
 | 
			
		||||
 | 
			
		||||
def getVertexPositions(obj):
 | 
			
		||||
    vertex_positions_list = []
 | 
			
		||||
 | 
			
		||||
    for vertex in obj.vtx:
 | 
			
		||||
        vertex_positions_list.append(vertex.getPosition(space='world'))
 | 
			
		||||
 | 
			
		||||
    return vertex_positions_list
 | 
			
		||||
 | 
			
		||||
def checkPlaneCollision(objPos, childPos, colPlane):
 | 
			
		||||
 | 
			
		||||
    v_coords = getVertexPositions(colPlane)
 | 
			
		||||
 | 
			
		||||
    collision_plane_matrix = pm.xform(colPlane, worldSpace=1, matrix=1, q=1)
 | 
			
		||||
    n = dt.Vector(collision_plane_matrix[4:7])    # Y axis direction of plane
 | 
			
		||||
    q = v_coords[1]
 | 
			
		||||
    d = n.dot(q)
 | 
			
		||||
 | 
			
		||||
    # get obj distance to plane
 | 
			
		||||
    toPlaneDistance = dist_to_plane(objPos, n, d)
 | 
			
		||||
    toPlaneDistance_child = dist_to_plane(childPos, n, d)
 | 
			
		||||
 | 
			
		||||
    # child projection position on plane
 | 
			
		||||
    projectPos_child = proj_pt_to_plane(childPos, n, d)
 | 
			
		||||
 | 
			
		||||
    inPlane = False
 | 
			
		||||
 | 
			
		||||
    if ckeckPointInTri(projectPos_child, v_coords[0], v_coords[1], v_coords[2]):
 | 
			
		||||
        inPlane = True
 | 
			
		||||
    elif ckeckPointInTri(projectPos_child, v_coords[3], v_coords[1], v_coords[2]):
 | 
			
		||||
        inPlane = True
 | 
			
		||||
 | 
			
		||||
    # bone above plane and bone child under plane and child project point on plane
 | 
			
		||||
    # means has collision with plane
 | 
			
		||||
    if (toPlaneDistance > 0) and (toPlaneDistance_child < 0) and inPlane:
 | 
			
		||||
        return projectPos_child
 | 
			
		||||
    else:
 | 
			
		||||
        return None
 | 
			
		||||
							
								
								
									
										476
									
								
								Scripts/Animation/springmagic/ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,476 @@
 | 
			
		||||
import os
 | 
			
		||||
import time
 | 
			
		||||
import inspect
 | 
			
		||||
import webbrowser
 | 
			
		||||
try:
 | 
			
		||||
    import urllib.request as urllib
 | 
			
		||||
except:
 | 
			
		||||
    import urllib2 as urllib
 | 
			
		||||
import random
 | 
			
		||||
import datetime
 | 
			
		||||
import maya.mel as mel
 | 
			
		||||
import pymel.core as pm
 | 
			
		||||
 | 
			
		||||
import Animation.springmagic.core as core
 | 
			
		||||
 | 
			
		||||
from shutil import copyfile
 | 
			
		||||
 | 
			
		||||
kSpringMagicVersion = 30500
 | 
			
		||||
 | 
			
		||||
scriptName = inspect.getframeinfo(inspect.currentframe()).filename
 | 
			
		||||
scriptPath = os.path.dirname(os.path.abspath(scriptName))
 | 
			
		||||
 | 
			
		||||
# Parameter Initialization
 | 
			
		||||
ui_file = scriptPath + os.sep + 'springMagic.ui'
 | 
			
		||||
 | 
			
		||||
# Constants
 | 
			
		||||
kPaypalLink = r'https://www.paypal.me/Yanbin'
 | 
			
		||||
kLinkedinLink = r'https://ca.linkedin.com/in/baiyanbin'
 | 
			
		||||
kVimeoLink = r''
 | 
			
		||||
kBilibiliLink = r'https://animbai.com/zh/2017/10/14/skintools-tutorials/'
 | 
			
		||||
kYoutubeLink = r'https://animbai.com/2017/10/14/skintools-tutorials/'
 | 
			
		||||
 | 
			
		||||
kUpdateLink = r'https://animbai.com/category/download/'
 | 
			
		||||
kVersionCheckLink = r'http://animbai.com/skintoolsver/'
 | 
			
		||||
kOldPersonalLink = r'http://www.scriptspot.com/3ds-max/scripts/spring-magic'
 | 
			
		||||
 | 
			
		||||
def widgetPath(windowName, widgetNames):
 | 
			
		||||
    """
 | 
			
		||||
    @param windowName: Window instance name to search
 | 
			
		||||
    @param widgetNames: list of names to search for
 | 
			
		||||
    """
 | 
			
		||||
    returnDict = {}
 | 
			
		||||
    mayaWidgetList = pm.lsUI(dumpWidgets=True)
 | 
			
		||||
 | 
			
		||||
    for widget in widgetNames:
 | 
			
		||||
        for mayaWidget in mayaWidgetList:
 | 
			
		||||
            if windowName in mayaWidget:
 | 
			
		||||
                if mayaWidget.endswith(widget):
 | 
			
		||||
                    returnDict[widget] = mayaWidget
 | 
			
		||||
 | 
			
		||||
    return returnDict
 | 
			
		||||
 | 
			
		||||
class SpringMagicWidget():
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.init()
 | 
			
		||||
 | 
			
		||||
    def init(self):
 | 
			
		||||
        try:
 | 
			
		||||
            pm.deleteUI(self.ui)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # title = pm.window(pm.loadUI(ui_file = ui_file))
 | 
			
		||||
 | 
			
		||||
        self.ui = pm.loadUI(f=ui_file)
 | 
			
		||||
 | 
			
		||||
        ui_widget_list = [
 | 
			
		||||
            'donatePayPal_button',
 | 
			
		||||
            'main_progressBar',
 | 
			
		||||
            'main_processLabel',
 | 
			
		||||
            'main_textEdit',
 | 
			
		||||
            'main_lang_id',
 | 
			
		||||
            'spring_language_list',
 | 
			
		||||
            'springSpring_lineEdit',
 | 
			
		||||
            'springSubs_lineEdit',
 | 
			
		||||
            'springXspring_lineEdit',
 | 
			
		||||
            'springTension_lineEdit',
 | 
			
		||||
            'springExtend_lineEdit',
 | 
			
		||||
            'springInertia_lineEdit',
 | 
			
		||||
            'springSubDiv_lineEdit',
 | 
			
		||||
            'springLoop_checkBox',
 | 
			
		||||
            'springPoseMatch_checkBox',
 | 
			
		||||
            'springClearSubFrame_checkBox',
 | 
			
		||||
            'springFrom_lineEdit',
 | 
			
		||||
            'springEnd_lineEdit',
 | 
			
		||||
            'springActive_radioButton',
 | 
			
		||||
            'springFrom_radioButton',
 | 
			
		||||
            # 'springUpAxis_comboBox',
 | 
			
		||||
            'springApply_Button',
 | 
			
		||||
            'springCapsule_checkBox',
 | 
			
		||||
            'springFastMove_checkBox',
 | 
			
		||||
            'springFloor_checkBox',
 | 
			
		||||
            'springFloor_lineEdit',
 | 
			
		||||
            'springBindPose_button',
 | 
			
		||||
            'springStraight_button',
 | 
			
		||||
            'springCopy_button',
 | 
			
		||||
            'springPaste_button',
 | 
			
		||||
            # 'donateBitcoin_lineEdit',
 | 
			
		||||
            'miscUpdate_pushButton',
 | 
			
		||||
            'springAddBody_Button',
 | 
			
		||||
            'springClearBody_Button',
 | 
			
		||||
            'springAddPlane_Button',
 | 
			
		||||
            'springAddWindCmd',
 | 
			
		||||
            'springBind_Button',
 | 
			
		||||
            'springBake_Button',
 | 
			
		||||
            'shelf_button',
 | 
			
		||||
            'link_pushButton',
 | 
			
		||||
            'vimeo_pushButton',
 | 
			
		||||
            'bilibili_pushButton',
 | 
			
		||||
            'language_button',
 | 
			
		||||
            'statusbar',
 | 
			
		||||
            'springWind_Button']
 | 
			
		||||
 | 
			
		||||
        self.uiObjects = widgetPath(self.ui, ui_widget_list)
 | 
			
		||||
 | 
			
		||||
        # Main UI
 | 
			
		||||
        self.main_progressBar = pm.progressBar(self.uiObjects['main_progressBar'], edit=True)
 | 
			
		||||
        self.main_processLabel = pm.text(self.uiObjects['main_processLabel'], edit=True)
 | 
			
		||||
        self.main_lineEdit = pm.ui.PyUI(self.uiObjects['main_textEdit'], edit=True)
 | 
			
		||||
        self.lang_id = pm.text(self.uiObjects['main_lang_id'], edit=True)
 | 
			
		||||
 | 
			
		||||
        self.language_list = pm.textScrollList(self.uiObjects['spring_language_list'], edit=True, selectCommand=self.languageSelectedCmd, visible=False)
 | 
			
		||||
 | 
			
		||||
        self.spring_lineEdit = pm.textField(self.uiObjects['springSpring_lineEdit'], edit=True, changeCommand=self.springRatioChangeCmd)
 | 
			
		||||
        self.subs_lineEdit = pm.textField(self.uiObjects['springSubs_lineEdit'], edit=True)
 | 
			
		||||
        self.Xspring_lineEdit = pm.textField(self.uiObjects['springXspring_lineEdit'], edit=True, changeCommand=self.twistChangeCmd)
 | 
			
		||||
        self.tension_lineEdit = pm.textField(self.uiObjects['springTension_lineEdit'], edit=True, changeCommand=self.tensionChangeCmd)
 | 
			
		||||
        self.extend_lineEdit = pm.textField(self.uiObjects['springExtend_lineEdit'], edit=True, changeCommand=self.extendChangeCmd)
 | 
			
		||||
        self.inertia_lineEdit = pm.textField(self.uiObjects['springInertia_lineEdit'], edit=True, changeCommand=self.inertiaChangeCmd)
 | 
			
		||||
        self.sub_division_lineEdit = pm.textField(self.uiObjects['springSubDiv_lineEdit'], edit=True, changeCommand=self.subDivChangeCmd)
 | 
			
		||||
        self.loop_checkBox = pm.checkBox(self.uiObjects['springLoop_checkBox'], edit=True)
 | 
			
		||||
        self.pose_match_checkBox = pm.checkBox(self.uiObjects['springPoseMatch_checkBox'], edit=True)
 | 
			
		||||
        self.clear_subframe_checkBox = pm.checkBox(self.uiObjects['springClearSubFrame_checkBox'], edit=True)
 | 
			
		||||
        self.from_lineEdit = pm.textField(self.uiObjects['springFrom_lineEdit'], edit=True)
 | 
			
		||||
        self.end_lineEdit = pm.textField(self.uiObjects['springEnd_lineEdit'], edit=True)
 | 
			
		||||
        self.active_radioButton = pm.radioButton(self.uiObjects['springActive_radioButton'], edit=True)
 | 
			
		||||
        self.from_radioButton = pm.radioButton(self.uiObjects['springFrom_radioButton'], edit=True)
 | 
			
		||||
        # self.upAxis_comboBox = pm.optionMenu(self.uiObjects['springUpAxis_comboBox'], edit=True)
 | 
			
		||||
        self.apply_button = pm.button(self.uiObjects['springApply_Button'], edit=True, command=self.applyCmd)
 | 
			
		||||
        self.add_body_button = pm.button(self.uiObjects['springAddBody_Button'], edit=True, command=self.addBodyCmd)
 | 
			
		||||
        self.clear_body_button = pm.button(self.uiObjects['springClearBody_Button'], edit=True, command=self.clearBodyCmd)
 | 
			
		||||
        self.add_plane_button = pm.button(self.uiObjects['springAddPlane_Button'], edit=True, command=self.createColPlaneCmd)
 | 
			
		||||
        self.wind_button = pm.button(self.uiObjects['springWind_Button'], edit=True, command=self.addWindCmd)
 | 
			
		||||
        self.bind_button = pm.button(self.uiObjects['springBind_Button'], edit=True, command=self.bindControlsCmd)
 | 
			
		||||
        self.bake_button = pm.button(self.uiObjects['springBake_Button'], edit=True, command=self.clearBindCmd)
 | 
			
		||||
        self.shelf_button = pm.button(self.uiObjects['shelf_button'], edit=True, command=self.goShelfCmd)
 | 
			
		||||
        self.link_button = pm.button(self.uiObjects['link_pushButton'], edit=True, command=self.linkinCmd)
 | 
			
		||||
        self.vimeo_button = pm.button(self.uiObjects['vimeo_pushButton'], edit=True, command=self.youtubeCmd)
 | 
			
		||||
        self.bilibili_button = pm.button(self.uiObjects['bilibili_pushButton'], edit=True, command=self.bilibiliCmd)
 | 
			
		||||
        self.language_button = pm.button(self.uiObjects['language_button'], edit=True, command=self.languageCmd)
 | 
			
		||||
 | 
			
		||||
        self.collision_checkBox = pm.checkBox(self.uiObjects['springCapsule_checkBox'], edit=True)
 | 
			
		||||
        self.fast_move_checkBox = pm.checkBox(self.uiObjects['springFastMove_checkBox'], edit=True)
 | 
			
		||||
        self.floor_checkBox = pm.checkBox(self.uiObjects['springFloor_checkBox'], edit=True)
 | 
			
		||||
        self.floor_lineEdit = pm.textField(self.uiObjects['springFloor_lineEdit'], edit=True, changeCommand=self.twistChangeCmd)
 | 
			
		||||
 | 
			
		||||
        self.bind_pose_button = pm.button(self.uiObjects['springBindPose_button'], edit=True, command=self.setCmd)
 | 
			
		||||
        self.straight_button = pm.button(self.uiObjects['springStraight_button'], edit=True, command=self.straightCmd)
 | 
			
		||||
        self.copy_button = pm.button(self.uiObjects['springCopy_button'], edit=True, command=self.copyCmd)
 | 
			
		||||
        self.paste_button = pm.button(self.uiObjects['springPaste_button'], edit=True, command=self.pasteCmd)
 | 
			
		||||
 | 
			
		||||
        # self.statusbar = pm.button(self.uiObjects['statusbar'], edit=True, menuItemCommand=self.testCmd)
 | 
			
		||||
 | 
			
		||||
        # donate UI
 | 
			
		||||
        # self.donate_bitcoin_lineEdit = pm.textField(self.uiObjects['donateBitcoin_lineEdit'], edit=True, text=kBitcoin)
 | 
			
		||||
 | 
			
		||||
        self.misc_update_button = pm.button(self.uiObjects['miscUpdate_pushButton'], edit=True, command=self.updatePageCmd)
 | 
			
		||||
 | 
			
		||||
        # SpringMagic_mainWindow11|centralwidget|miscUpdate_pushButton
 | 
			
		||||
        # SpringMagic_mainWindow11|centralwidget|miscUpdate_pushButton
 | 
			
		||||
        # 'miscUpdate_pushButton': ui.Button('SpringMagic_mainWindow11|centralwidget|miscUpdate_pushButton')
 | 
			
		||||
        # miscUpdate_button = pm.button( centralwidget + '|miscUpdate_pushButton', edit = True )
 | 
			
		||||
 | 
			
		||||
        pm.button(self.uiObjects['donatePayPal_button'], edit=True, command=self.donatePayPalCmd)
 | 
			
		||||
 | 
			
		||||
        self.spam_word = ['', '', '', '', '']
 | 
			
		||||
 | 
			
		||||
    def show(self):
 | 
			
		||||
 | 
			
		||||
        pm.showWindow(self.ui)
 | 
			
		||||
 | 
			
		||||
        self.checkUpdate()
 | 
			
		||||
 | 
			
		||||
    def progression_callback(self, progression):
 | 
			
		||||
        pm.progressBar(self.main_progressBar, edit=True, progress=progression)
 | 
			
		||||
 | 
			
		||||
    #############################################
 | 
			
		||||
    # Buttons callbacks
 | 
			
		||||
    ############################################
 | 
			
		||||
 | 
			
		||||
    def showSpam(self, *args):
 | 
			
		||||
        sWord = self.spam_word[random.randint(0, 4)]
 | 
			
		||||
        # print as unicode
 | 
			
		||||
        pm.text(self.main_processLabel, edit=True, label=str(sWord))
 | 
			
		||||
 | 
			
		||||
    def linkinCmd(self, *args):
 | 
			
		||||
        # open my linked in page :)
 | 
			
		||||
        url = kLinkedinLink
 | 
			
		||||
 | 
			
		||||
        webbrowser.open(url, new=2)
 | 
			
		||||
 | 
			
		||||
    def pasteCmd(self, *args):
 | 
			
		||||
        core.pasteBonePose()
 | 
			
		||||
 | 
			
		||||
    def setCmd(self, *args):
 | 
			
		||||
 | 
			
		||||
        picked_bones = pm.ls(sl=1, type='joint')
 | 
			
		||||
 | 
			
		||||
        if picked_bones:
 | 
			
		||||
            self.apply_button.setEnable(False)
 | 
			
		||||
 | 
			
		||||
            core.bindPose()
 | 
			
		||||
 | 
			
		||||
            # Select only the joints
 | 
			
		||||
            pm.select(picked_bones)
 | 
			
		||||
 | 
			
		||||
            self.apply_button.setEnable(True)
 | 
			
		||||
 | 
			
		||||
    def straightCmd(self, *args):
 | 
			
		||||
 | 
			
		||||
        picked_bones = pm.ls(sl=1, type='joint')
 | 
			
		||||
 | 
			
		||||
        if picked_bones:
 | 
			
		||||
            self.apply_button.setEnable(False)
 | 
			
		||||
 | 
			
		||||
            for bone in picked_bones:
 | 
			
		||||
                core.straightBonePose(bone)
 | 
			
		||||
 | 
			
		||||
            # Select only the joints
 | 
			
		||||
            pm.select(picked_bones)
 | 
			
		||||
 | 
			
		||||
            self.apply_button.setEnable(True)
 | 
			
		||||
 | 
			
		||||
    def applyCmd(self, *args):
 | 
			
		||||
        picked_transforms = pm.ls(sl=1, type='transform')
 | 
			
		||||
 | 
			
		||||
        if picked_transforms:
 | 
			
		||||
            self.apply_button.setEnable(False)
 | 
			
		||||
 | 
			
		||||
            pm.text(self.main_processLabel, edit=True, label='Calculating Bone Spring... (Esc to cancel)')
 | 
			
		||||
 | 
			
		||||
            springRatio = 1 - float(self.spring_lineEdit.getText())
 | 
			
		||||
            twistRatio = 1 - float(self.Xspring_lineEdit.getText())
 | 
			
		||||
            isLoop = bool(self.loop_checkBox.getValue())
 | 
			
		||||
            isPoseMatch = bool(self.pose_match_checkBox.getValue())
 | 
			
		||||
            isFastMove = self.fast_move_checkBox.getValue()
 | 
			
		||||
            isCollision = self.collision_checkBox.getValue()
 | 
			
		||||
 | 
			
		||||
            subDiv = 1.0
 | 
			
		||||
            if isCollision:
 | 
			
		||||
                subDiv = float(self.sub_division_lineEdit.getText())
 | 
			
		||||
 | 
			
		||||
            # get frame range
 | 
			
		||||
            if self.active_radioButton.getSelect():
 | 
			
		||||
                startFrame = int(pm.playbackOptions(q=1, minTime=1))
 | 
			
		||||
                endFrame = int(pm.playbackOptions(q=1, maxTime=1))
 | 
			
		||||
            else:
 | 
			
		||||
                startFrame = int(self.from_lineEdit.getText())
 | 
			
		||||
                endFrame = int(self.end_lineEdit.getText())
 | 
			
		||||
 | 
			
		||||
            tension = float(self.tension_lineEdit.getText())
 | 
			
		||||
            inertia = float(self.inertia_lineEdit.getText())
 | 
			
		||||
            extend = float(self.extend_lineEdit.getText())
 | 
			
		||||
 | 
			
		||||
            wipeSubFrame = self.clear_subframe_checkBox.getValue()
 | 
			
		||||
 | 
			
		||||
            spring = core.Spring(springRatio, twistRatio, tension, extend, inertia)
 | 
			
		||||
            springMagic = core.SpringMagic(startFrame, endFrame, subDiv, isLoop, isPoseMatch, isCollision, isFastMove, wipeSubFrame)
 | 
			
		||||
 | 
			
		||||
            startTime = datetime.datetime.now()
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                core.startCompute(spring, springMagic, self.progression_callback)
 | 
			
		||||
 | 
			
		||||
                deltaTime = (datetime.datetime.now() - startTime)
 | 
			
		||||
 | 
			
		||||
                pm.text(self.main_processLabel, edit=True, label="Spring Calculation Time: {0}s".format(deltaTime.seconds))
 | 
			
		||||
 | 
			
		||||
            except ValueError as exception:
 | 
			
		||||
                pm.text(self.main_processLabel, edit=True, label='Process aborted')
 | 
			
		||||
                pm.warning(exception)
 | 
			
		||||
 | 
			
		||||
            # Select only the joints
 | 
			
		||||
            pm.select(picked_transforms)
 | 
			
		||||
 | 
			
		||||
            pm.progressBar(self.main_progressBar, edit=True, progress=0)
 | 
			
		||||
 | 
			
		||||
            self.apply_button.setEnable(True)
 | 
			
		||||
 | 
			
		||||
    def copyCmd(self, *args):
 | 
			
		||||
        core.copyBonePose()
 | 
			
		||||
 | 
			
		||||
    def webCmd(self, *args):
 | 
			
		||||
        # open my linked in page :)
 | 
			
		||||
        webbrowser.open(kOldPersonalLink, new=2)
 | 
			
		||||
 | 
			
		||||
    def twistChangeCmd(self, *args):
 | 
			
		||||
        self.limitTextEditValue(self.Xspring_lineEdit, defaultValue=0.7)
 | 
			
		||||
 | 
			
		||||
    def extendChangeCmd(self, *args):
 | 
			
		||||
        self.limitTextEditValue(self.extend_lineEdit, defaultValue=0.0)
 | 
			
		||||
 | 
			
		||||
    def inertiaChangeCmd(self, *args):
 | 
			
		||||
        self.limitTextEditValue(self.inertia_lineEdit, defaultValue=0.0)
 | 
			
		||||
 | 
			
		||||
    def springRatioChangeCmd(self, *args):
 | 
			
		||||
        self.limitTextEditValue(self.spring_lineEdit, defaultValue=0.7)
 | 
			
		||||
 | 
			
		||||
    def tensionChangeCmd(self, *args):
 | 
			
		||||
        self.limitTextEditValue(self.tension_lineEdit, defaultValue=0.5)
 | 
			
		||||
 | 
			
		||||
    def subDivChangeCmd(self, *args):
 | 
			
		||||
        # self.limitTextEditValue(self.sub_division_lineEdit, defaultValue=1)
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def addWindCmd(self, *args):
 | 
			
		||||
        core.addWindObj()
 | 
			
		||||
 | 
			
		||||
    def addBodyCmd(self, *args):
 | 
			
		||||
        core.addCapsuleBody()
 | 
			
		||||
 | 
			
		||||
    def createColPlaneCmd(self, *args):
 | 
			
		||||
        core.createCollisionPlane()
 | 
			
		||||
 | 
			
		||||
    def removeBodyCmd(self, *args):
 | 
			
		||||
        core.removeBody(clear=False)
 | 
			
		||||
 | 
			
		||||
    def clearBodyCmd(self, *args):
 | 
			
		||||
        core.removeBody(clear=True)
 | 
			
		||||
 | 
			
		||||
    def bindControlsCmd(self, *args):
 | 
			
		||||
        core.bindControls()
 | 
			
		||||
 | 
			
		||||
    def clearBindCmd(self, *args):
 | 
			
		||||
 | 
			
		||||
        # get frame range
 | 
			
		||||
        if self.active_radioButton.getSelect():
 | 
			
		||||
            startFrame = int(pm.playbackOptions(q=1, minTime=1))
 | 
			
		||||
            endFrame = int(pm.playbackOptions(q=1, maxTime=1))
 | 
			
		||||
        else:
 | 
			
		||||
            startFrame = int(self.from_lineEdit.getText())
 | 
			
		||||
            endFrame = int(self.end_lineEdit.getText())
 | 
			
		||||
 | 
			
		||||
        core.clearBind(startFrame, endFrame)
 | 
			
		||||
 | 
			
		||||
    def goShelfCmd(self, *args):
 | 
			
		||||
        parentTab = mel.eval('''global string $gShelfTopLevel;string $shelves = `tabLayout -q -selectTab $gShelfTopLevel`;''')
 | 
			
		||||
        imageTitlePath = scriptPath + os.sep + "icons" + os.sep + "Title.png"
 | 
			
		||||
        # commandLine = "execfile(r'{0}\\springMagic.py')".format(self.scriptPath)
 | 
			
		||||
        commandLine = "try:\n\timport springmagic\n\tspringmagic.main()\nexcept:\n\texecfile(r'{0}\springMagic.py')".format(scriptPath)
 | 
			
		||||
 | 
			
		||||
        pm.shelfButton(commandRepeatable=True, image1=imageTitlePath, label="Spring Magic", parent=parentTab, command=commandLine)
 | 
			
		||||
 | 
			
		||||
    def languageCmd(self, *args):
 | 
			
		||||
        self.language_list.setVisible(not self.language_list.getVisible())
 | 
			
		||||
 | 
			
		||||
    def languageSelectedCmd(self, *args):
 | 
			
		||||
        self.language_list.setVisible(False)
 | 
			
		||||
        self.applyLanguage(int(self.language_list.getSelectIndexedItem()[0]))
 | 
			
		||||
 | 
			
		||||
    def bilibiliCmd(self, *args):
 | 
			
		||||
        try:
 | 
			
		||||
            webbrowser.open(kBilibiliLink, new=2)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def youtubeCmd(self, *args):
 | 
			
		||||
        try:
 | 
			
		||||
            webbrowser.open(kYoutubeLink, new=2)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def vimeoCmd(self, *args):
 | 
			
		||||
        # try:
 | 
			
		||||
        #     webbrowser.open(kVimeoLink, new=2)
 | 
			
		||||
        # except:
 | 
			
		||||
        #     pass
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def donatePayPalCmd(self, *args):
 | 
			
		||||
        try:
 | 
			
		||||
            webbrowser.open(kPaypalLink, new=2)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def updatePageCmd(self, *args):
 | 
			
		||||
        try:
 | 
			
		||||
            webbrowser.open(kUpdateLink, new=2)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def applyLanguage(self, lanId):
 | 
			
		||||
        lanDict = {1: '_chn', 2: '_eng', 3: '_jpn'}
 | 
			
		||||
 | 
			
		||||
        if lanId in lanDict.keys():
 | 
			
		||||
            # get new language ui file path
 | 
			
		||||
            new_ui_file = scriptPath + os.sep + os.path.basename(ui_file).split('.')[0] + lanDict[lanId] + '.' + os.path.basename(ui_file).split('.')[1]
 | 
			
		||||
            copyfile(new_ui_file, ui_file)
 | 
			
		||||
 | 
			
		||||
            # Reload interface
 | 
			
		||||
            self.init()
 | 
			
		||||
            self.show()
 | 
			
		||||
 | 
			
		||||
    def detectMayaLanguage(self):
 | 
			
		||||
        mayaLan = None
 | 
			
		||||
        try:
 | 
			
		||||
            mayaLan = os.environ['MAYA_UI_LANGUAGE']
 | 
			
		||||
        except:
 | 
			
		||||
            import locale
 | 
			
		||||
            mayaLan = locale.getdefaultlocale()[0]
 | 
			
		||||
 | 
			
		||||
        lanDict = {'zh_CN': 1, 'en_US': 2, 'ja_JP': 3}
 | 
			
		||||
        self.applyLanguage(lanDict[mayaLan])
 | 
			
		||||
 | 
			
		||||
    def printTextEdit(self, textEdit, inputString):
 | 
			
		||||
        ctime = time.ctime()
 | 
			
		||||
        ptime = ctime.split(' ')
 | 
			
		||||
        inputString = ptime[3] + '  -  ' + inputString
 | 
			
		||||
        pm.scrollField(textEdit, edit=True, insertionPosition=0, insertText=inputString + '\n')
 | 
			
		||||
 | 
			
		||||
    def checkUpdate(self):
 | 
			
		||||
 | 
			
		||||
        self.misc_update_button.setVisible(0)
 | 
			
		||||
 | 
			
		||||
        page_content = None
 | 
			
		||||
 | 
			
		||||
        site = kVersionCheckLink
 | 
			
		||||
        hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
 | 
			
		||||
               'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 | 
			
		||||
               'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
 | 
			
		||||
               'Accept-Encoding': 'none',
 | 
			
		||||
               'Accept-Language': 'en-US,en;q=0.8',
 | 
			
		||||
               'Connection': 'keep-alive'}
 | 
			
		||||
 | 
			
		||||
        req = urllib.Request(site, headers=hdr)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            page = urllib.urlopen(req, timeout=5)
 | 
			
		||||
            page_content = page.read()
 | 
			
		||||
        except:
 | 
			
		||||
            print('checkUpdate failed')
 | 
			
		||||
 | 
			
		||||
        if page_content:
 | 
			
		||||
            page_content = page_content.decode('utf-8')
 | 
			
		||||
            if len(page_content.split('|springMagic|')) > 1:
 | 
			
		||||
                new_kSpringMagicVersion = int(page_content.split('|springMagic|')[1])
 | 
			
		||||
 | 
			
		||||
                if new_kSpringMagicVersion > kSpringMagicVersion:
 | 
			
		||||
                    self.misc_update_button.setVisible(1)
 | 
			
		||||
 | 
			
		||||
                self.spam_word = []
 | 
			
		||||
 | 
			
		||||
                prefix = '|spam'
 | 
			
		||||
                suffix = '|'
 | 
			
		||||
 | 
			
		||||
                if self.lang_id.getLabel() == 'chn':
 | 
			
		||||
                    suffix = 'chn|'
 | 
			
		||||
 | 
			
		||||
                self.spam_word = [page_content.split(prefix + str(i) + suffix)[1] for i in range(1, 6)]
 | 
			
		||||
        else:
 | 
			
		||||
            pm.text(self.main_processLabel, edit=True, label='Check update failed, try later.')
 | 
			
		||||
 | 
			
		||||
        self.showSpam()
 | 
			
		||||
 | 
			
		||||
    def limitTextEditValue(self, ui_object, minValue=0, maxValue=1, roundF=2, defaultValue=0):
 | 
			
		||||
        value = 0
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            value = float(ui_object.getText())
 | 
			
		||||
            value = round(value, roundF)
 | 
			
		||||
            value = max(min(maxValue, value), minValue)
 | 
			
		||||
        except:
 | 
			
		||||
            value = defaultValue
 | 
			
		||||
 | 
			
		||||
        ui_object.setText(str(value))
 | 
			
		||||
							
								
								
									
										50
									
								
								Scripts/Animation/springmagic/utility.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,50 @@
 | 
			
		||||
import pymel.core as pm
 | 
			
		||||
import pymel.core.datatypes as dt
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
# Usefull function
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
def clamp(n, minn, maxn):
 | 
			
		||||
    return max(min(maxn, n), minn)
 | 
			
		||||
 | 
			
		||||
def get_node(name):
 | 
			
		||||
    node_list = pm.ls(name)
 | 
			
		||||
    node = None
 | 
			
		||||
 | 
			
		||||
    if node_list:
 | 
			
		||||
        node = node_list[0]
 | 
			
		||||
 | 
			
		||||
    return node
 | 
			
		||||
 | 
			
		||||
def get_matrix(obj):
 | 
			
		||||
    return pm.xform(obj, worldSpace=1, matrix=1, q=1)
 | 
			
		||||
 | 
			
		||||
def frange(start, stop=None, step=None):
 | 
			
		||||
    # if set start=0.0 and step = 1.0 if not specified
 | 
			
		||||
    start = float(start)
 | 
			
		||||
 | 
			
		||||
    if stop is None:
 | 
			
		||||
        stop = start + 0.0
 | 
			
		||||
        start = 0.0
 | 
			
		||||
 | 
			
		||||
    if step is None:
 | 
			
		||||
        step = 1.0
 | 
			
		||||
 | 
			
		||||
    # print("start = ", start, "stop = ", stop, "step = ", step)
 | 
			
		||||
 | 
			
		||||
    count = 0
 | 
			
		||||
    while True:
 | 
			
		||||
        temp = float(start + count * step)
 | 
			
		||||
        if step > 0 and temp >= stop:
 | 
			
		||||
            break
 | 
			
		||||
        elif step < 0 and temp <= stop:
 | 
			
		||||
            break
 | 
			
		||||
        yield temp
 | 
			
		||||
        count += 1
 | 
			
		||||
 | 
			
		||||
def get_translation(n):
 | 
			
		||||
    return dt.Vector(pm.xform(n, worldSpace=1, translation=1, query=1))
 | 
			
		||||
 | 
			
		||||
def get_rotation(n):
 | 
			
		||||
    return pm.xform(n, worldSpace=1, rotation=1, query=1)
 | 
			
		||||
							
								
								
									
										18
									
								
								Scripts/Animation/springmagic/安装说明.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
			
		||||
可以用 2 种方法来安装 spring magic Maya 版本
 | 
			
		||||
 | 
			
		||||
A:
 | 
			
		||||
    1. 解压缩 springmagic.zip 至任意目录, 但保持所有文件在 "springmagic" 目录下 ( 不要改变大小写 )
 | 
			
		||||
    2. 在 Maya 里运行如下 Python 命令, 会出现工具界面 ( 注意保留前面的小写 r )
 | 
			
		||||
        execfile(r'你的路径\springmagic\springMagic.py')
 | 
			
		||||
    例如
 | 
			
		||||
        execfile(r'D:\myScript\springmagic\springMagic.py')
 | 
			
		||||
    3. 用工具界面右上方的创建快捷按钮功能,在书签栏创建一个快捷按钮,方便下次使用
 | 
			
		||||
 | 
			
		||||
B:
 | 
			
		||||
    1. 解压缩 springmagic.zip 并复制 "springmagic" 目录到位于 Windows 用户路径下的 Maya 脚本目录
 | 
			
		||||
    例如
 | 
			
		||||
        "C:\Users\你的用户名\Documents\maya\scripts"
 | 
			
		||||
    2. 在 Maya 里运行如下 Python 命令, 会出现工具界面
 | 
			
		||||
        import springmagic
 | 
			
		||||
        springmagic.main()
 | 
			
		||||
    3. 用工具界面右上方的创建快捷按钮功能,在书签栏创建一个快捷按钮,方便下次使用
 | 
			
		||||