【功能描述】:选择蒙皮上的点,自动创建骨骼蒙皮,然后会在前面创建样条滑块来控制这个骨骼移动,达到滑块控制表情的效果。
https://blog.csdn.net/zszwebart/article/details/137632084?spm=1011.2415.3001.5331 这是之前的版本,这是后来的
rollout FaceRig "面部表情控制器24.12.26" width:184 height:150 rolledUp:true
(
------------///////////UI设置///////////////---------
GroupBox grp1 "拾取头骨" pos: [3,3] width:179 height:129
dropdownlist SetAsix "" pos: [140,0] width:30 height:30 items:#("world", "parent","local")
label asix "切换坐标->" pos:[80,3] width:60 height:15
spinner bonepos "骨骼位移幅度" pos: [55,15] width:80 height:15 range: [0,100,3]
pickbutton BindHeadName "拾取自定义头骨(CS用默认)" pos: [5,30] width:175 height:19
button btnGenerate "生成控制器" pos: [5,49] width:87 height:30 enabled:false
button btnDel "删除控制器" pos: [92,49] width:87 height:30 enabled:false
button FreeZeeBorder "冻结范围块" pos: [5,78] width:87 height:22 range: [0,10,1]
button FreeZeeSkinModel "冻结模型" pos: [92,78] width:87 height:22 range: [0,10,1]
checkbutton SkinMode "skin模式" pos: [5,100] width:53 height:30 checked:false
checkbutton FigureMode "<形体模式>" pos: [58,100] width:70 height:30 checked:false
button ResetSlider "归位滑块" pos: [125,100] width:55 height:30 checked:false
------------///////////全局变量///////////////---------
global Index = 1
global HeadBone = undefined
global SizeScale = 1
global BoneName="Bone_ctrl" + (Index as string)
global RectangleName="Border_ctrl" + (Index as string)
global CircleName="Handle_ctrl" + (Index as string)
global headCenterName="headCenter_ctrl"+(Index as string)
global CtrlPackageName="Package_ctrl"+(Index as string)
global SelVertexID=#()
------------///////////函数///////////////---------
--搜索具有ctrl名字的找到最大值
fn FindIndex =(
objs = objects
-- 创建一个空列表用于存放符合条件的对象
ctrlObjs = for obj in objs where ((findstring (obj.name as string) "ctrl")!= undefined ) collect obj
if (ctrlObjs.count > 0)then(
allNumbers = #()
for ctrlobj in ctrlObjs do (
-- 提取已存在带ctrl节点名称中的数字部分
local numlist = #("1","2","3","4","5","6","7","8","9")
for i in numlist do(
local numberList = findstring ctrlobj.name i
if (numberList!= undefined ) then
(
local mynumber = (substring ctrlobj.name numberList -1)as string
append allNumbers mynumber
)
)
)
-- 先假设列表中第一个数字转换后的数值为最大值(前提是列表至少有一个数字)
maxNumber = allNumbers[1] as number
for num in allNumbers do
(
local numValue = num as number
if (numValue > maxNumber) then
(
maxNumber = numValue
)
)
return #(ctrlObjs.count,maxNumber)
)
else(
return #(ctrlObjs.count,1 )--从1开始,不能改为0会出错
)
)
--搜索具有Border名字的冻结它de开关
fn FindBorderFrozen =(
objs = objects
-- 创建一个空列表用于存放符合条件的对象
ctrlObjs = for obj in objs where ((findstring (obj.name as string) "Border")!= undefined ) collect obj
for ctrlobj in ctrlObjs do (
if(ctrlobj.isfrozen ==false)then(
freeze ctrlobj
FreeZeeBorder.text="解冻范围块"
)else(
unfreeze ctrlobj
FreeZeeBorder.text="冻结范围块"
)
)
)
--搜索skin模型冻结它de开关
fn FindSkinFrozen =(
objs = geometry
-- 创建一个空列表用于存放符合条件的对象
SkinObjs = for obj in objs where (obj.modifiers[#skin]!= undefined ) collect obj
for SkinObj in SkinObjs do (
if(SkinObj.isfrozen ==false)then(
freeze SkinObj
FreeZeeSkinModel.text="解冻模型"
)else(
unfreeze SkinObj
FreeZeeSkinModel.text="冻结模型"
)
)
)
--搜索骨骼冻结它de开关
fn FindBoneFrozen =(
objs = geometry
-- 创建一个空列表用于存放符合条件的对象
joints = for obj in objs where (classof obj==BoneGeometry ) collect obj
for joint in joints do (
if(joint.isfrozen ==false)then(
freeze joint
FreeZeeSkinModel.text="解冻模型"
)else(
unfreeze joint
FreeZeeSkinModel.text="冻结模型"
)
)
)
--选择顶点的id号和数量
fn SelVertex&IDNum obj NumAllVerts SkinMod SelVertexID selectedVertsPosSum= (
AveragePos =[0,0,0]
SelectedVertsCount =0
for i = 1 to NumAllVerts do (
-- 检查顶点是否被选中
if (skinOps.IsVertexSelected SkinMod i ) then (
--加入选择的顶点id到集合
appendIfUnique SelVertexID i
-- 获取顶点位置
if(classof obj==PolyMeshObject)then(vertPos = polyop.getVert obj i)
else if(classof obj==PolyMeshObject)then(vertPos = meshop.getVert obj i)
-- 将顶点位置累加到总和中
selectedVertsPosSum[1] += vertPos.x
selectedVertsPosSum[2] += vertPos.y
selectedVertsPosSum[3] += vertPos.z
-- 增加所选顶点的计数
SelectedVertsCount += 1
AveragePos = [(selectedVertsPosSum[1]/SelectedVertsCount), (selectedVertsPosSum[2]/SelectedVertsCount), (selectedVertsPosSum[3]/SelectedVertsCount)]
)
)
return #(SelVertexID,SelectedVertsCount,AveragePos)
)
------------///////////窗体///////////////---------
--打开窗口时对系统尺寸的规范化
on GenerateControllersRollout open do
(
---系统单位CM厘米标准化
units.Systemtype = #centimeters
units.SystemScale = 1
units.Displaytype = #metric
units.MetricType = #centimeters
SetGridSpacing 1
Index = 1
)
------------///////////按钮///////////////---------
--清空变量
on bnFreeAll pressed do(
freeAll()
print ("选择模型为:" +obj as string)
print ("选择顶点的ID为:" +SelVertexID as string)
print ("选择顶点的数量为:" +SelectedVertsCount as string)
print ("选择顶点的平均位置为:" +AveragePos as string)
)
--更改坐标的下拉菜单
on SetAsix selected i do(
i=SetAsix .selection
if(i==1)then(toolMode.coordsys #world)
else if(i==2)then(toolMode.coordsys #parent)
else(toolMode.coordsys #local)
)
--拾取头骨名的按钮
on BindHeadName picked obj do
(
BindHeadName.text = obj.name
if(BindHeadName.text!="拾取自定义头骨(CS用默认)")then(
FigureMode.enabled=false
SkinMode.enabled=true
btnGenerate.enabled=true
btnDel.enabled=true)
)
--产生控制器的按钮(主要功能)
on btnGenerate pressed do
(
-- try(
-- 获取应用了Skin修改器的对象
local obj = selection[1]
HeadBone =BindHeadName.text
if(BindHeadName.text!="拾取自定义头骨(CS用默认)")then(
aaa = BindHeadName.text
HeadBone = getnodebyname aaa
if(headbone ==undefined)do(
messagebox("请拾取正确的头骨")
)
)else(
HeadBone = $*head[1]
if(headbone ==undefined)do(
messagebox("请拾取正确的头骨")
)
)
--求出模型高度
if (selection[1]!=undefined and (classof selection[1] == PolyMeshObject or classof selection[1]== Editable_mesh ) and headbone !=undefined ) then (
bbox = nodeLocalBoundingBox obj
dim = (bbox[2] - bbox[1]) * obj.scale
dim = [abs(dim[1]), abs(dim[2]), abs(dim[3])] --反馈出物体的长,深,高
--objWidth= dim[1]
--objDepth= dim[2]
objHigh= dim[3]
--设定缩放系数和控制器ctrl的偏移值
SizeScale = objHigh/170 --从标准人体高度1米7获取缩放比
local CtrlOffset= 50*SizeScale
-- 获取Skin修改器
local SkinMod = obj.modifiers[#skin]
-- 获取所有顶点数量
local NumAllVerts = skinOps.GetNumberVertices SkinMod
-- 初始化用于累积所选顶点位置的总和
local selectedVertsPosSum = [0,0,0]
-- 初始化用于计数所选顶点的数量
local SelectedVertsCount = 0
-- 初始化平均位置
local AveragePos =[0,0,0]
-- 初始化ctl
local ctrl = undefined
-- 调用SelVertex&IDNum函数,根据选择的是mesh还是poly物体来判断,遍历所有顶点,更新所选顶点SelectedVertsCount的值
aa = SelVertex&IDNum obj NumAllVerts SkinMod SelVertexID selectedVertsPosSum
SelVertexID = aa[1]
SelectedVertsCount = aa[2]
AveragePos = aa[3]
-- 假如选择的顶点SelectedVertsCount非空则执行以下动作
if (SelectedVertsCount > 0) then (
--计算出选择的顶点平均位置
noloop = true --防止死循环
bb = FindIndex()
if(bb[1]>0)then(Index=bb[2]+1)else(Index=bb[2])
BoneName="Bone_ctrl" + (Index as string)
RectangleName="Border_ctrl" + (Index as string)
CircleName="Handle_ctrl" + (Index as string)
headCenterName="headCenter_ctrl"+(Index as string)
if (noloop ==true and BoneName != undefined and RectangleName != undefined and CircleName != undefined and SelectedVertsCount > 0) do (
--创建bone
local MyBone = boneSys.createBone AveragePos AveragePos AveragePos
NewBoneName=BoneName
--创建Rectangle
MyRectangleName=RectangleName
RectangleSize = SizeScale*10
-- 首先获取头骨HeadBone对象,求出头骨的矩阵
targetTM = HeadBone.transform
-- /////以头骨的局部坐标Z轴向外移动50,先将方向向量转换到头骨的局部坐标系下/////
-- 创建一个变换矩阵
myRotationMatrix = (eulerAngles -90 0 0) as matrix3
-- 创建一个平移矩阵的方向向量,先平移矩阵再乘以旋转矩阵 这样平移的方位才不会受旋转的影响。
headOffsetTM=([0,10,0]*targetTM)-HeadBone.transform.position
myTranslationMatrix = transMatrix (AveragePos+headOffsetTM)
-- 将旋转矩阵和平移矩阵组合
local CtrlBone_Matrix = myRotationMatrix * myTranslationMatrix
-- 创建Rectangle,设置相关属性,位置使用移动后的位置movedPos
local MyRectangle = Rectangle length:RectangleSize width:RectangleSize cornerRadius:SizeScale name:MyRectangleName wirecolor:(color 250 230 100)
MyRectangle.transform = CtrlBone_Matrix --这个不能放在参数里指定
--求出头骨的中心
bbox = nodeLocalBoundingBox HeadBone
centerX = (bbox[1].x + bbox[2].x) / 2
centerY = (bbox[1].y + bbox[2].y) / 2
centerZ = (bbox[1].z + bbox[2].z) / 2
-- 输出中心坐标给一个虚拟体
local headCenter = $headCenter*[1]
if(headCenter==undefined)do(
local headCenter=Dummy length:10 width:10 hight:10 name:headCenterName
headCenter.position=[centerX,centerY,centerZ]
headCenter.parent=HeadBone
)
-- 方框范围控制器的目标约束到头骨中心的虚拟体
MyRectangle.rotation.controller = LookAt_Constraint ()
B = MyRectangle.rotation.controller
B.appendtarget headCenter 50
B.target_axis = 2
B.upnode_ctrl = 0
B.StoUP_axis = 1
B.lookat_vector_length = 0
---创建一个控制包
local CtrlPackage=$Package_ctrl*[1]
if(CtrlPackage==undefined)do(
CtrlPackage=Point pos:[0,0,0] name:CtrlPackageName
)
--创建circle
MyCircleName=CircleName
CircleSize=SizeScale*2
local MyCircle = circle radius:CircleSize name:MyCircleName wirecolor:(color 250 230 100)
ctrl = getnodebyname MyCircleName
Index += 1 --------------------Index累加---------------------
----设置bone的size---
MyBone.name = NewBoneName
MyBone.width = SizeScale
MyBone.height = SizeScale
MyBone.length = SizeScale
MyBone.parent = HeadBone
---设置Rectangle---
MyRectangle.parent = CtrlPackage --原来是HeadBone
-- 设置Rectangle的x,y,z轴不能移动
Lock = #{}
join lock #{1,2,3,4,5,7,8,9}
setTransformLockFlags MyRectangle Lock
------提取控制器Rectangle到控制包Package_ctrl1---------
------提取控制器Rectangle---------
--MyPosition = in coordsys HeadBone (MyRectangle.position)
-- 给控制边框MyRectangle添加链接约束
MyRectangle.transform.controller = Link_Constraint ()
c = MyRectangle.transform.controller
c.AddTarget HeadBone 0
------提取控制器Rectangle到控制包end---------
---设置circle---
MyCircle.parent = MyRectangle
in coordsys parent (MyCircle.pos =[0,0,0])
in coordsys parent (MyCircle.rotation =(EulerAngles 0 0 0))
-- 设置circle的z轴不能移动
Lock = #{}
join lock #{3, 4, 5, 6, 7, 8, 9}
setTransformLockFlags MyCircle Lock
if( NewBoneName != undefined and MyRectangleName != undefined and MyCircleName != undefined and SelectedVertsCount > 0 )then(
------------- 以下是设置bone,cycle,Rectangle的控制器功能--------------
--指定一个reactor控制器到box001的位置控制器上。
reactorCtrl=MyBone.position.controller =position_Reactor()
-- 将控制器的连接到$box002的位置上
reactorCtrl.reactions.reactTo MyCircle.Position.controller --默认就会创建第一个状态
reactorCtrl.reactions.setName 1 "one"
Handle_ctrl_Pos=(in coordsys MyRectangle(MyCircle.position))
reactorCtrl.reactions.setValueAsVector 1 (Handle_ctrl_Pos-(in coordsys MyRectangle([0,(MyRectangle.width/2),0])))
Bone_ctrl_Pos=(in coordsys headbone(MyBone.position))
reactorCtrl.reactions.setVectorState 1 (Bone_ctrl_Pos+(in coordsys headbone([bonepos.value,0,0])))
reactorCtrl.reactions.create name: "two" --创建第二个状态
reactorCtrl.reactions.setValueAsVector 2 (Handle_ctrl_Pos+(in coordsys MyRectangle([0,(MyRectangle.width/2),0])))
reactorCtrl.reactions.setVectorState 2 (Bone_ctrl_Pos-(in coordsys headbone([bonepos.value,0,0])))
reactorCtrl.reactions.setInfluence 1 100
reactorCtrl.reactions.setInfluence 2 100
-- 将骨骼MyBone添加到Skin修改器的骨骼列表中
skinOps.addBone obj.modifiers[#Skin] MyBone 1
local boneNames = for i = 1 to (skinOps.GetNumberBones SkinMod) collect (skinOps.GetBoneName SkinMod i 0)
local boneIndexInList = findItem boneNames NewBoneName
-- 遍历选中的顶点,给它们设置权重为50%(0.5)
for i = 1 to NumAllVerts do (
if (skinOps.IsVertexSelected SkinMod i ) then (
-- 设置顶点对于骨骼NewBone的权重,这里假设Skin修改器有类似setVertexWeight的函数来设置权重(需根据实际调整)
skinOps.SetVertexWeights SkinMod i boneIndexInList 0.5
)
)
ctrl.position.controller = Position_List()
local controllers = ctrl.position.controller
--创建script控制器
controllers[2].controller = position_script ()
--创建ctrl的l,w,x,y自定义属性
local def = attributes ctrl
(
parameters ctrlP
(
l type:#float
w type:#float
x type:#float
y type:#float
)
)
custAttributes.add controllers[2].controller def
def = custAttributes.getdef controllers[2].controller.ctrl
-- 获取对象的位置轨迹下的script控制器,填入内容
if controllers.count >= 2 and controllers[2].name == "Position Script" do
(
local codeContent = "x=0\ny=0\nl=this.l/2\nw=this.w/2\nif this.x>w then(x=-this.x+w;this.x=w)\nif this.x<-w then(x=-this.x-w;this.x=-w)\nif this.y>l then(y=-this.y+l;this.y=l)\nif this.y<-l then(y=-this.y-l;this.y=-l)\n[ x, y, 0 ]"
controllers[2].script = codeContent
)
--创建wire约束,让圆圈不能超出方形的边界
local A = MyRectangle.baseObject[#Length]
local B = ctrl.position.controller[2].controller.ctrl[#l]
paramWire.connect A B "Length"
A = MyRectangle.baseObject[#Width]
B = ctrl.position.controller[2].controller.ctrl[#w]
paramWire.connect A B "Width"
A = ctrl.position.controller[1].controller[#X_Position]
B = ctrl.position.controller[2].controller.ctrl[#x]
paramWire.connect A B "X_Position"
A = ctrl.position.controller[1].controller[#Y_Position]
B = ctrl.position.controller[2].controller.ctrl[#y]
paramWire.connect A B "Y_Position"
print("创建完毕!")
btnDel.text="删除控制器"
btnDel.enabled =true
)
else(
FindIndex= FindIndex()
if(FindIndex[1]==0)then(
btnDel.text="已经清完"
btnDel.enabled =false
)
print("场景中已有带ctrl名的控制器及骨骼,请检查序号,设置'开始的序号':从尾号后一位开始")
)
ctrl = undefined
------------- 设置bone,cycle,Rectangle的控制器功能end--------------
aa = false --防止死循环
)
)
else (
print "没有选中顶点。"
)
obj = undefined
)
else(
messagebox ("请选择一个模型")
)
-- )--tryEnd
-- catch()
)
--冻结边框的按钮
on FreeZeeBorder pressed do
(
FindBorderFrozen()
)
--冻结皮肤和模型的按钮
on FreeZeeSkinModel pressed do
(
FindSkinFrozen()
FindBoneFrozen()
)
--删除控制器的按钮
on btnDel pressed do
(
try(
bb= FindIndex()
if(bb[1]>0)then(Index=bb[2])else(Index=bb[2])
BoneName="Bone_ctrl" + (Index as string)
RectangleName="Border_ctrl" + (Index as string)
CircleName="Handle_ctrl" + (Index as string)
headCenterName="headCenter_ctrl"+(Index as string)
CtrlPackageName="Package_ctrl"+(Index as string)
toolMode.coordsys #world
for i=index to 1 by -1 do (
if (getnodebyname BoneName != undefined)do(
delete (getnodebyname BoneName)
)
if getnodebyname RectangleName != undefined do(
delete (getnodebyname RectangleName)
)
if (getnodebyname CircleName != undefined)do(
aaa= getnodebyname CircleName
in coordsys parent (aaa.position =[0,0,0])
delete aaa
)
if (getnodebyname headCenterName != undefined)do(
delete (getnodebyname headCenterName)
)
if (getnodebyname CtrlPackageName != undefined)do(
delete (getnodebyname CtrlPackageName)
)
)
if(bb[1]==0)then(
btnDel.text="已经清完"
btnDel.enabled =false)
)
catch()
)
--skin模式的按钮
on skinMode changed state do(
max modify mode
if state == on then (
if($.modifiers[#Skin]!=undefined)then(
modPanel.setCurrentObject $.modifiers[#Skin]
subobjectLevel = 1
-- 更新文本
SkinMode.text="退出蒙皮"
)else(messagebox"请选择蒙皮")
)
else (
if($.modifiers[#Skin]!=undefined)then(
modPanel.setCurrentObject $.modifiers[#Skin]
subobjectLevel = 0
--更新文本
SkinMode.text="skin模式"
)else(messagebox"请选择蒙皮")
)
)
--figure模式的按钮
on FigureMode changed state do (
bipObj = $Bip*[1].controller
if state == on then (
FigureMode.text="<退出形体>"
btnGenerate.enabled=true
btnDel.enabled=true
bipObj.figureMode = true
)
else (
FigureMode.text="<形体模式>"
bipObj.figureMode = false
btnGenerate.enabled=false
btnDel.enabled=false
)
)
--归位滑块的按钮
on ResetSlider pressed do(
try(
if(findstring (selection[1].name as string) "Handle"!= undefined )then(
aaa= selection[1]
in coordsys parent (aaa.position =[0,0,0])
)
)catch()
)
)