maxscript 面部表情样条控制器

【功能描述】:选择蒙皮上的点,自动创建骨骼蒙皮,然后会在前面创建样条滑块来控制这个骨骼移动,达到滑块控制表情的效果。



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()
	)
)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值