You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
20 KiB
Python
445 lines
20 KiB
Python
#
|
|
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
# its licensors.
|
|
#
|
|
# For complete copyright and license terms please see the LICENSE at the root of this
|
|
# distribution (the "License"). All use of this software is governed by the License,
|
|
# or, if provided, by the license below or the license accompanying this file. Do not
|
|
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
#
|
|
from maya import cmds
|
|
import math
|
|
import maya.mel
|
|
|
|
#defines the shape labels
|
|
g_shapeTypes = ['box', 'capsule', 'sphere']
|
|
g_materialNames = ['mat_proxy_phys', 'no material']
|
|
g_boxIndex = 0
|
|
g_capsuleIndex = 1
|
|
g_sphereIndex = 2
|
|
|
|
g_orientTypes = ['x', 'y', 'z', 'xCenter', 'yCenter', 'zCenter']
|
|
g_defaultOrientTypeIndex = 0
|
|
g_xIndex = 0
|
|
g_yIndex = 1
|
|
g_zIndex = 2
|
|
g_xCenterIndex = 3
|
|
g_yCenterIndex = 4
|
|
g_zCenterIndex = 5
|
|
g_centerString = 'Center'
|
|
|
|
##########
|
|
##The colors that will be used in the editor
|
|
##########
|
|
g_UIColor = [0.165, 0.165, 0.165]
|
|
|
|
g_defaultMaterialColor = [0, 1, 0]
|
|
|
|
##########
|
|
##Width and height definitions for ui elements
|
|
##########
|
|
g_uiProxyWidthColumnWidth = 30
|
|
g_controlHeight = 22
|
|
g_listboxHeight = 35
|
|
g_buttonSpace = 4
|
|
g_rowItemHeight = 22
|
|
|
|
g_uiShapePulldownColumnWidth = 75
|
|
g_uiOrientPulldownColumnWidth = 50
|
|
g_uiMaterialNamePulldownWidth = 120
|
|
g_uiMaterialFieldColumnWidth = 125
|
|
g_scrollBarPadding = 36
|
|
|
|
g_buttonMarginPadding = 10
|
|
g_marginWidth = 5
|
|
g_boxWidth = 400
|
|
|
|
g_uiColumnWidth = [g_uiProxyWidthColumnWidth, g_uiShapePulldownColumnWidth, g_uiOrientPulldownColumnWidth, g_uiMaterialFieldColumnWidth]
|
|
|
|
|
|
#TODO: make width/height dynamic
|
|
g_windowHeight = 120
|
|
g_windowWidth = 318
|
|
|
|
#default name of display layer where proxies will reside
|
|
g_nameOfDisplayLayer = 'lyr_lumberyard_phys'
|
|
|
|
#postfix of the physics proxy
|
|
g_physPostfix = '_phys';
|
|
|
|
#the default width of proxies
|
|
g_proxyShapeDefaultWidth = 10
|
|
|
|
#estimating that the proxy width is 1/10th of the hip height
|
|
g_proxyWidthRatioToHeight = 0.1
|
|
|
|
class ProxyObject(object):
|
|
|
|
##This class represents both the variables and the ui of the proxy editor
|
|
def __init__(self) :
|
|
|
|
#defines the ui of the width text field
|
|
self.uiTextFieldWidth = ''
|
|
|
|
#defines the value of the width and depth
|
|
self.width = g_proxyShapeDefaultWidth
|
|
|
|
#defines the ui of the shape drop down ui
|
|
self.menuDropDownShape = ''
|
|
|
|
#defines this proxy's shape
|
|
self.shape = g_shapeTypes[g_boxIndex]
|
|
|
|
#defines this proxies material name
|
|
self.proxyMaterialName = ''
|
|
|
|
#defines the ui used to modify orientation
|
|
self.menuDropDownOrient = ''
|
|
|
|
#defines the orientation of the proxy
|
|
self.objectOrient = g_orientTypes[g_defaultOrientTypeIndex]
|
|
|
|
self.menuDropDownMaterialName = ''
|
|
|
|
proxyCreationSettings = ProxyObject()
|
|
|
|
|
|
def LumberyardProxyTool():
|
|
##Create the main window. If it exists, delete it, and then create it
|
|
if cmds.window('LUMBERYARDTOOL_PROXY_WINDOW', exists=True):
|
|
cmds.deleteUI('LUMBERYARDTOOL_PROXY_WINDOW')
|
|
|
|
LumberyardProxyToolUI()
|
|
|
|
def LumberyardProxyToolUI():
|
|
##This script defines the window or primary canvas of the ui.
|
|
lumberyardProxyWindow = cmds.window('LUMBERYARDTOOL_PROXY_WINDOW', title = "Proxy Tool (Experimental)", sizeable = False, iconName = 'Proxy Tool', widthHeight = (g_windowWidth, g_windowHeight))
|
|
LumberyardToolCreateProxyFrameMain()
|
|
cmds.showWindow(lumberyardProxyWindow)
|
|
cmds.window(lumberyardProxyWindow, edit = True, widthHeight = (g_windowWidth, g_windowHeight))
|
|
|
|
def LumberyardToolCreateProxyFrameMain():
|
|
##When the window is first open this is what the user will see
|
|
cmds.columnLayout('LUMBERYARDTOOL_PROXY_LAYOUT', adjustableColumn = True)
|
|
|
|
cmds.text(height = g_controlHeight, enable = True, font = 'tinyBoldLabelFont', align = 'center', width = g_uiProxyWidthColumnWidth+3, label = 'Select joints and click "Create Proxies"', backgroundColor = g_UIColor)
|
|
|
|
cmds.setParent('LUMBERYARDTOOL_PROXY_WINDOW')
|
|
|
|
cmds.rowLayout(numberOfColumns = 4, columnWidth4 = g_uiColumnWidth, backgroundColor = g_UIColor)
|
|
|
|
#title labels for all the variable categories that we'll have control over
|
|
cmds.text(height = g_controlHeight, enable = True, font = 'tinyBoldLabelFont', align = 'left', width = g_uiProxyWidthColumnWidth+3, label = 'width', backgroundColor = g_UIColor)
|
|
cmds.text(height = g_controlHeight, enable = True, font = 'tinyBoldLabelFont', align = 'left', width = g_uiShapePulldownColumnWidth, label = 'shape', backgroundColor = g_UIColor)
|
|
cmds.text(height = g_controlHeight, enable = True, font = 'tinyBoldLabelFont', align = 'left', width = g_uiOrientPulldownColumnWidth+25, label = 'orient', backgroundColor = g_UIColor)
|
|
cmds.text(height = g_controlHeight, enable = True, font = 'tinyBoldLabelFont', align = 'left', width = g_uiMaterialFieldColumnWidth + g_marginWidth, label = 'material name', backgroundColor = g_UIColor)
|
|
|
|
##This is the editable area of the ui that will be populated by our variables. In the beginning it is unpopulated but has instruction on what to do
|
|
cmds.setParent('LUMBERYARDTOOL_PROXY_LAYOUT')
|
|
cmds.scrollLayout ('LUMBERYARDTOOL_PROXY_LISTBOX', height = g_listboxHeight, width = g_boxWidth , backgroundColor = [0, 0, 0], visible = True)
|
|
|
|
cmds.setParent('LUMBERYARDTOOL_PROXY_LISTBOX')
|
|
|
|
#create a row
|
|
cmds.rowLayout(numberOfColumns = 4, columnWidth4 = g_uiColumnWidth)
|
|
|
|
#create the width ui
|
|
LumberyardCreateWidthTextField()
|
|
|
|
#create the shape ui
|
|
LumberyardCreateShapePullDown()
|
|
|
|
#create the orient ui
|
|
LumberyardCreateOrientPullDown()
|
|
|
|
LumberyardCreateMaterialPullDown()
|
|
|
|
#create the ui underneath the editable variables window. This is the canvas for our create button
|
|
cmds.setParent('LUMBERYARDTOOL_PROXY_LAYOUT')
|
|
cmds.setParent('..')
|
|
cmds.rowColumnLayout(numberOfRows = 1, width = g_boxWidth , columnSpacing = [1, g_buttonSpace])
|
|
cmds.button(label = 'Create Proxies', width = g_windowWidth-4, command = LumberyardCreateProxies, backgroundColor = g_UIColor)
|
|
|
|
def LumberyardCreateWidthTextField():
|
|
##This function creates the ui element that defines the width and depth of the proxy
|
|
|
|
#get the width of the proxy from the class
|
|
uiTextFieldProxyWidthValue = g_proxyShapeDefaultWidth
|
|
|
|
#create the ui text field
|
|
proxyCreationSettings.uiTextFieldWidth = cmds.textField(width = g_uiProxyWidthColumnWidth , backgroundColor = g_UIColor, text = uiTextFieldProxyWidthValue)
|
|
|
|
#add a command that will add functionality when the textfield is changed
|
|
cmds.textField(proxyCreationSettings.uiTextFieldWidth, edit = True, changeCommand = lambda *args: LumberyardChangeProxyWidth(cmds.textField(proxyCreationSettings.uiTextFieldWidth, query = True, text = True)))
|
|
|
|
LumberyardChangeProxyWidth(g_uiProxyWidthColumnWidth)
|
|
|
|
def LumberyardChangeProxyWidth(widthInput):
|
|
##This function is run when the width text field is updated
|
|
|
|
#get current width
|
|
currentWidth = proxyCreationSettings.width
|
|
|
|
#determine if this entry is a float
|
|
if LumberyardFloatConfirm(widthInput) == True:
|
|
|
|
if float(widthInput) > 0:
|
|
#if the entry is a float and greater than zero, update the class with the new width
|
|
proxyCreationSettings.width = widthInput
|
|
|
|
else:
|
|
#zero or negative floats are not a valid width value, so revert to previous value
|
|
cmds.textField(proxyCreationSettings.uiTextFieldWidth, edit = True, text = currentWidth)
|
|
|
|
else:
|
|
#if the entry is not a float, revert it to its previous value
|
|
cmds.textField(proxyCreationSettings.uiTextFieldWidth, edit = True, text = currentWidth)
|
|
|
|
def LumberyardCreateOrientPullDown():
|
|
##This function creates the ui pulldown for orientation
|
|
|
|
#create the ui pulldown
|
|
orientOptionPullDown = cmds.optionMenu(label = '', width = g_uiShapePulldownColumnWidth, backgroundColor = g_UIColor, changeCommand = lambda *args: LumberyardProxySetOrientType())
|
|
|
|
#add it to the class
|
|
proxyCreationSettings.menuDropDownOrient = orientOptionPullDown
|
|
|
|
#populate the pulldown with the predefined orient types
|
|
for orient in g_orientTypes:
|
|
cmds.menuItem('button_' + orient, label = orient)
|
|
|
|
LumberyardProxySetOrientType()
|
|
|
|
def LumberyardProxySetOrientType():
|
|
##This function is launched when the orient value is changed in the editor
|
|
|
|
#get the new value from the menu
|
|
orientString = (cmds.optionMenu(proxyCreationSettings.menuDropDownOrient, query = True, value = True))
|
|
|
|
#store the new value in the class
|
|
proxyCreationSettings.objectOrient = orientString
|
|
|
|
def LumberyardCreateShapePullDown():
|
|
##This function creates the ui pulldown for shape types in the editor
|
|
|
|
#create the ui pulldown
|
|
shapeOptionPullDown = cmds.optionMenu(label = '', width = g_uiShapePulldownColumnWidth, backgroundColor = g_UIColor,changeCommand = lambda *args: LumberyardProxySetShapeType())
|
|
|
|
#add it to the class
|
|
proxyCreationSettings.menuDropDownShape = shapeOptionPullDown
|
|
|
|
#populate the pulldown with the predefined shape types
|
|
for shapeType in g_shapeTypes:
|
|
cmds.menuItem('button_' + shapeType, label = shapeType)
|
|
|
|
LumberyardProxySetShapeType()
|
|
|
|
def LumberyardCreateMaterialPullDown():
|
|
##This function creates the ui pulldown for material name
|
|
|
|
#create the ui pulldown
|
|
materialOptionPullDown = cmds.optionMenu(label = '', width = g_uiMaterialFieldColumnWidth, backgroundColor = g_UIColor, changeCommand = lambda *args: LumberyardProxySetMaterialName())
|
|
|
|
#add it to the class
|
|
proxyCreationSettings.menuDropDownMaterialName = materialOptionPullDown
|
|
|
|
#populate the pulldown with the predefined materialName types
|
|
for materialName in g_materialNames :
|
|
cmds.menuItem('button_' + materialName, label = materialName)
|
|
|
|
LumberyardProxySetMaterialName()
|
|
|
|
def LumberyardProxySetMaterialName():
|
|
|
|
materialNameString = (cmds.optionMenu(proxyCreationSettings.menuDropDownMaterialName, query = True, value = True))
|
|
|
|
proxyCreationSettings.proxyMaterialName = materialNameString
|
|
|
|
def LumberyardProxySetShapeType():
|
|
##This function is launched when the shape value is changed in the editor
|
|
|
|
#get the new value from the menu
|
|
shapeString = (cmds.optionMenu(proxyCreationSettings.menuDropDownShape, query = True, value = True))
|
|
|
|
#store the new value in the class
|
|
proxyCreationSettings.shape = shapeString
|
|
|
|
def LumberyardAddMaterialToProxy(inputProxy):
|
|
##This function adds a material to a proxy shape object
|
|
|
|
#get the material name from the class
|
|
thisMaterialName = proxyCreationSettings.proxyMaterialName
|
|
|
|
#get the shading group
|
|
shadingGroup = thisMaterialName + 'SG'
|
|
|
|
#variable that defines the shader
|
|
shader = ''
|
|
|
|
#if the material does not exist, create a shader group that supports it
|
|
if cmds.objExists(thisMaterialName) == False:
|
|
shader = cmds.shadingNode('phong', asShader = True, name = thisMaterialName)
|
|
#add attribute that will define this proxy material
|
|
cmds.addAttr(shader, longName = 'lumberyardPhysMaterial', defaultValue = 1)
|
|
shadingGroup = cmds.sets(renderable = True, noSurfaceShader = True, empty = True, name = shadingGroup)
|
|
cmds.connectAttr(shader +'.outColor', shadingGroup + '.surfaceShader')
|
|
cmds.setAttr(shader + '.color', g_defaultMaterialColor[0], g_defaultMaterialColor[1], g_defaultMaterialColor[2], type = 'double3')
|
|
else:
|
|
shader = 'thisMaterialName'
|
|
#apply the shading group to the proxy object
|
|
cmds.sets(inputProxy[0], edit = True, forceElement = shadingGroup )
|
|
|
|
def CreateProxyPositionFromChildren(jointNode):
|
|
#get the children. the joint's children define the length of the proxy
|
|
jointChildren = cmds.listRelatives(jointNode, children = True, path = True, type = 'joint')
|
|
|
|
#get the joint node's position in world space
|
|
jointStartPosition = cmds.xform(jointNode, query = True, worldSpace = True, translation = True)
|
|
|
|
#get the average joint position for all of the child joints this will determine the proxy's length
|
|
sumOfJointPositionX = 0.0
|
|
sumOfJointPositionY = 0.0
|
|
sumOfJointPositionZ = 0.0
|
|
proxyPositionX = jointStartPosition[0]
|
|
proxyPositionY = jointStartPosition[1]
|
|
proxyPositionZ = jointStartPosition[2]
|
|
|
|
#if a joint has children, find out the length of the average child joints
|
|
if (jointChildren):
|
|
for jointChild in jointChildren:
|
|
jointPositionWorldSpace = cmds.xform( jointChild, query = True, worldSpace = True, translation = True)
|
|
sumOfJointPositionX = sumOfJointPositionX + jointPositionWorldSpace[0]
|
|
sumOfJointPositionY = sumOfJointPositionY + jointPositionWorldSpace[1]
|
|
sumOfJointPositionZ = sumOfJointPositionZ + jointPositionWorldSpace[2]
|
|
averageJointPositionX = sumOfJointPositionX / len(jointChildren)
|
|
averageJointPositionY = sumOfJointPositionY / len(jointChildren)
|
|
averageJointPositionZ = sumOfJointPositionZ / len(jointChildren)
|
|
#move to the midpoint of joint and averaged child joints
|
|
proxyPositionX = ((averageJointPositionX + jointStartPosition[0])/2)
|
|
proxyPositionY = ((averageJointPositionY + jointStartPosition[1])/2)
|
|
proxyPositionZ = ((averageJointPositionZ + jointStartPosition[2])/2)
|
|
|
|
proxyPosition = [proxyPositionX, proxyPositionY, proxyPositionZ]
|
|
return proxyPosition
|
|
|
|
def LumberyardCreateProxies(*args):
|
|
##This function creates the proxy objects and parents them to joints
|
|
|
|
#create display layers for proxy's visibility to easily be toggled
|
|
LumberyardCreateProxyLayer()
|
|
|
|
#variable for the display layer
|
|
layerPhys = g_nameOfDisplayLayer
|
|
|
|
#make a list of all of the selected objects
|
|
selectedObjects = cmds.ls(selection = True)
|
|
|
|
#if nothing is selected throw a warning
|
|
if (len(selectedObjects) == 0):
|
|
cmds.warning('No joints selected. Select Joints and press the Add Joints button.')
|
|
|
|
else:
|
|
for selectedObject in selectedObjects:
|
|
|
|
if cmds.objectType(selectedObject, isType = 'joint' ) == True:
|
|
|
|
shapeOfProxy = proxyCreationSettings.shape
|
|
|
|
#get the joint node's position in world space
|
|
jointStartPosition = cmds.xform(selectedObject, query = True, worldSpace = True, translation = True)
|
|
proxyPosition = jointStartPosition
|
|
proxyWidth = float(proxyCreationSettings.width)
|
|
proxyLength = proxyWidth
|
|
|
|
if (g_centerString in proxyCreationSettings.objectOrient) == False:
|
|
|
|
proxyPosition = CreateProxyPositionFromChildren(selectedObject)
|
|
|
|
#find out the length of the average child joints
|
|
distanceBetweenPoints = math.sqrt(math.pow((jointStartPosition[0] - proxyPosition[0]), 2) + math.pow((jointStartPosition[1] - proxyPosition[1]) ,2) + math.pow((jointStartPosition[2] - proxyPosition[2]), 2))
|
|
proxyLength = distanceBetweenPoints * 2
|
|
|
|
#create the proxy shape
|
|
if (shapeOfProxy == g_shapeTypes[g_boxIndex]):
|
|
proxyShape = cmds.polyCube(height = proxyLength, width = proxyWidth, depth = proxyWidth, createUVs = 1,constructionHistory = True)
|
|
else:
|
|
proxyRadius = proxyWidth/2
|
|
if (shapeOfProxy == g_shapeTypes[g_capsuleIndex]):
|
|
if (proxyLength > proxyWidth):
|
|
proxyShape = cmds.polyCylinder(radius = proxyRadius, height = proxyLength - (proxyRadius *2), roundCap = True, subdivisionsCaps = 3)
|
|
else:
|
|
proxyShape = cmds.polySphere(radius = proxyRadius * 2, subdivisionsAxis = 16, subdivisionsHeight = 16)
|
|
else:
|
|
proxyShape = cmds.polySphere(radius = proxyRadius * 2, subdivisionsAxis = 16, subdivisionsHeight = 16)
|
|
|
|
#add object to the phys display layer
|
|
cmds.editDisplayLayerMembers( layerPhys, proxyShape[0])
|
|
|
|
#add an attribute to easily find our created proxy
|
|
cmds.addAttr(proxyShape, longName = 'lumberyardPhysProxy', defaultValue = 1)
|
|
|
|
#add material to proxy
|
|
if (proxyCreationSettings.proxyMaterialName != 'no material'):
|
|
|
|
LumberyardAddMaterialToProxy(proxyShape)
|
|
|
|
#add orientation to proxy
|
|
LumberyardOrientProxy(proxyShape[0])
|
|
|
|
#move proxy into place
|
|
cmds.move(proxyPosition[0], proxyPosition[1], proxyPosition[2], proxyShape, worldSpace = True)
|
|
|
|
#orient the proxy so its aiming toward the parent joint. can we do this with vector math instead?
|
|
aimConsProxy = cmds.aimConstraint(selectedObject, proxyShape)
|
|
cmds.delete(aimConsProxy)
|
|
orientConsProxy = cmds.orientConstraint(selectedObject, proxyShape, skip = 'none')
|
|
cmds.delete(orientConsProxy)
|
|
|
|
#if the orient pulldown defines this as a center, center its orient on the joint.
|
|
if (g_centerString in proxyCreationSettings.objectOrient):
|
|
cmds.move(jointStartPosition[0], jointStartPosition[1],jointStartPosition[2], proxyShape[0], worldSpace = True)
|
|
else:
|
|
cmds.move(jointStartPosition[0], jointStartPosition[1], jointStartPosition[2], proxyShape[0] + ".scalePivot", proxyShape[0] + ".rotatePivot", worldSpace = True)
|
|
|
|
#parent it to the joint
|
|
cmds.parent(proxyShape[0], selectedObject)
|
|
|
|
#freeze the proxy. this will zero out its translate values relative to its parent joint
|
|
cmds.makeIdentity(proxyShape[0], apply = True, scale = 1, rotate = 1, translate = 1, normal = 0)
|
|
|
|
#delete construction history
|
|
cmds.delete(proxyShape[0],constructionHistory=True)
|
|
|
|
#rename the proxy with the proper name
|
|
cmds.rename(proxyShape[0], selectedObject + g_physPostfix)
|
|
else:
|
|
cmds.warning(selectedObject + " is not a joint. No proxy created.")
|
|
|
|
def LumberyardOrientProxy(proxyObjectInput):
|
|
##This function orients the proxy based on the settings in the editor ui
|
|
|
|
if proxyCreationSettings.objectOrient == g_orientTypes[g_xIndex] or proxyCreationSettings.objectOrient == g_orientTypes[g_xCenterIndex]:
|
|
cmds.xform(proxyObjectInput, rotateAxis = [0, 90, 90])
|
|
|
|
if proxyCreationSettings.objectOrient == g_orientTypes[g_yIndex] or proxyCreationSettings.objectOrient == g_orientTypes[g_yCenterIndex]:
|
|
cmds.xform(proxyObjectInput, rotateAxis = [0, 0, 0])
|
|
|
|
if proxyCreationSettings.objectOrient == g_orientTypes[g_zIndex] or proxyCreationSettings.objectOrient == g_orientTypes[g_zCenterIndex]:
|
|
cmds.xform(proxyObjectInput, rotateAxis = [90, 0, 0])
|
|
|
|
def LumberyardCreateProxyLayer():
|
|
##This function creates a layer for our proxy objects so visibility may be easily toggled
|
|
#if there is not already a display layer for the proxies, create one
|
|
if cmds.objExists(g_nameOfDisplayLayer) == False:
|
|
cmds.createDisplayLayer(name = g_nameOfDisplayLayer)
|
|
|
|
def LumberyardFloatConfirm(inputString):
|
|
##This function checks to see if the entered string is a float value and returns true or false
|
|
try:
|
|
float(inputString)
|
|
except ValueError:
|
|
return False
|
|
else:
|
|
return True
|
|
|