MaxScript编写bone转换biped工具

news2024/11/13 10:11:15

一、制作转换工具的缘由

  大家好,我是阿赵。我经常从各种渠道得到了一些角色模型,这些模型得到之后,会发现是带有蒙皮和骨骼,甚至带有动作的。
  不过这些资源很多都是从游戏截取出来的,导入到3DsMax之后,角色的骨骼是bone类型的,不是biped类型的。熟悉3DsMax的人应该都知道,bone类型还要重新的去自己连IK,如果骨骼是biped的话,使用起来会比较方便一点。所以我一直想找一个工具能把bone骨骼转换到biped骨骼。在网上找了一些工具,也能用,不过总感觉不是特别的顺手。所以我决定自己做一个。
  这个工具的完整代码在最后提供了。不过我要先说明,我是在3DsMax2018版本开发的这个工具,在不同版本对Maxscript的支持不一样。由于我是免费开源的,所以如果遇到API问题或者bug,请各位自行解决了。
  顺便说一句,如果各位有什么工具想开发的,也可以说一下,如果简单的,我可以免费做完当文章写提供开源。如果是复杂的,我也可以根据自己的时间来考虑是否收费制作。

二、工具的使用说明

1、需要的东西

在这里插入图片描述

  我这里拿一个阴阳师的游戏模型作为例子。这是一个带有蒙皮信息的角色模型的fbx文件,导入到了3DsMax里面。
在这里插入图片描述

  注意看一下场景物体列表,会发现,这个模型的骨骼,原来就是用biped做的,然后还附加了一些自定义的bone骨骼。
  这种本身是biped做的骨骼,需要还原成biped的话,相对来说会比较容易一些,因为它的结构和命名本身就是符合biped的规则的。
  不过很多角色资源的模型,它的骨骼并不是biped做的,因为制作3D资源的软件很多,并不一定是3Dsmax,也可以是maya等。所以我也要兼容其他的骨骼情况。

2、工具介绍界面

在这里插入图片描述

  运行了脚本之后,会看到出现一个界面,它是由5个可折叠窗口组成的。
  第一个是About窗口,它的内容首先是显示了作者信息,然后还有操作的简单步骤
  后面的4个界面,是对应接下来操作的4个大步骤的。接下来再一一说明

1.指定biped架构的骨骼

在这里插入图片描述

  这个工具窗口,是把作为要生成biped时必要的部位的骨骼指定的过程。可以看到上面有一个BoneList的列表,第一列里面列出了biped骨骼所有支持的骨骼部位,第二列用星号标出了哪些部位是必须的。因为作为biped的基础部位,如果没有这些骨骼,就不能生成。

这个指定的步骤可以分为自动和手动2种操作
(1)如果原来的骨骼就是biped制作的,那么可以选择骨骼的根节点,然后点击SelectAndFit按钮,就会自动的遍历原来所有的骨骼,并根据骨骼的名称,去匹配biped的部位
(2)如果原来的骨骼命名并不是按照biped规则的,也可以手动的去指定,具体的操作是选择骨骼的根节点,点击Select按钮,接下来,先在场景里面选择需要指定的骨骼物体,然后单击列表里面对应的骨骼部位。如果想取消某个骨骼的指定,可以双击列表里面的骨骼部位。
在这里插入图片描述

  针对第二种手动指定的情况,因为指定一次并不容易,所以我还提供了保存和读取的功能。当指定完成之后,可以点击Save按钮,这时候会打开一个保存文件的窗口,让用户选择需要保存的文件,文件使用csv格式。当下次想继续编辑的时候,先用Select按钮选择了骨骼的根节点,然后点击Load按钮,这时候会打开一个读取文件的选择窗口,选择之前保存的csv文件,就可以把上一次的结果读取进来了。

2.指定自定义骨骼

  由于角色的骨骼不一定全部都是biped的部位,还可以有很多自定义的骨骼部位,比如衣服的裙摆、武器的骨骼等,所以在指定了biped骨骼之后,接下来还可以指定自定义的骨骼。当然如果没有,也可以不指定。
在这里插入图片描述

  这个操作必须是在第一步biped骨骼指定完成或者读取完成之后,才能做的,因为这里还需要判断自定义的骨骼的父节点的类型。
操作也是分自动和手动2种情况
(1)如果想使用自定义功能,在指定完biped骨骼之后,还是选择原骨骼的根节点,然后点击Select And Check按钮,这时候,工具会遍历根节点下面的所有节点,如果不是之前已经指定过的biped骨骼,那么统一都认为是自定义骨骼
(2)也可以手动指定一些骨骼,然后点击Select And Add按钮加入到列表里面去。这个功能是有去重的,所以如果之前已经加过的骨骼,是不会重复添加的。
(3)点击Clean按钮,可以清除所有已经指定的自定义骨骼,也可以双击列表里面的骨骼名称,删除单个骨骼。
在这里插入图片描述

3.创建新骨骼

在指定好biped部位的骨骼和自定义骨骼之后,就可以生成新的biped骨骼了
在这里插入图片描述

  在Bone Controller面板,在输入了需要生成的Biped名字之后,可以选择One Key Create来一键创建Biped和自定义bone,然后自动对齐他们的位置
  如果不想一键也可以分步式的手动生成,用Create Biped Only按钮来单独生成Biped和用Create Bones Only按钮生成自定义骨骼bones,然后用ResizeBones按钮手动对齐骨骼位置。
如果之前已经生成过了,在接着操作的时候,也可以通过选择新Biped根节点,然后点击SelectBiped按钮,来指定已经生成的biped骨骼。
在这里插入图片描述

这是已经生成完biped和自定义骨骼之后的模型状态,可以看到,新生成的骨骼和原模型是刚好对齐了。

4.蒙皮操作

在生成了骨骼之后,就可以进行蒙皮的操作了
在这里插入图片描述
  由于一个模型有可能是由多个网格部位组成的,所以这里可以单选或者多选需要复制的蒙皮网格,然后点击Add按钮,添加到列表里面去。也可以选择已经添加的网格,然后点Remove按钮移除。
  最后点击ReSkin按钮,工具会复制已经选择的蒙皮网格,并且读取它们的蒙皮信息,并转移到新生成的骨骼上面去。
在这里插入图片描述

这是蒙皮之后的结果,可以看到,新生成的网格模型,已经可以准确的跟随着新生成的骨骼运动了。

三、工具的原理

这个版本的工具,是我特意按照原理的思路,把步骤拆分的,从工具可以看出,整个过程分为了以下步骤:
1、原始骨骼数据收集
2、生成新的Biped
3、对齐骨骼
4、获取蒙皮信息
5、重新蒙皮
下面具体的来说一下这些步骤的实现:

1、原始骨骼数据收集

在收集数据之前,我们起码要知道,有哪些数据是需要收集的。
先来看看MaxScript创建Biped的语句:
biped.createNew <height_float> <angle_float> <wpos_point3> . . .
有3个数据是必须的:
1.骨骼高度
2.旋转角度
3.世界坐标
然后后面的省略号,是一些可选参数,实际上,都是在3DsMax里面的Figure Mode里面的
骨骼参数:
在这里插入图片描述

在这里插入图片描述

我们怎么去知道这些参数呢?我们只能从原始的bone骨骼里面去猜出这些参数。所以必须先让用户指定原始bone的根骨骼,然后通过遍历的方式,去读取这些骨骼。

1.获取某种关节数量

从已经指定的Biped部位,可以判断出每种骨骼部位的数量,比如Spine骨骼,判断一下有多少个Spine部位已经指定了骨骼的,那么Spine的数量就能获得了,其他的手指、脚趾也一样
在这里插入图片描述

2.Triangle Pelvis和Triangle Neck

这两个选项,会导致骨骼的树形结构变化
如果勾上了Triangle Pelvis, L Thigh和R Thigh会在Spine下一层
在这里插入图片描述

如果不勾Triangle Pelvis,L Thigh和R Thigh会在Spine同一层,都是在Pelvis下一层
在这里插入图片描述

如果勾上了Triangle Neck, L Clavicle和 R Clavicle会在和Neck同一层
在这里插入图片描述

如果不勾Triangle Neck, L Clavicle和 R Clavicle会在和Neck的下一层
在这里插入图片描述

知道了这个关系之后,我们收集骨骼信息的时候,就要特殊留意这些骨骼的父子关系,看看是属于哪一种的。

3.高度怎样获取

实际上高度是没有办法获取的,因为这里的高度,是字面意思,是人骨骼的头顶到脚底的高度。但我们的原始骨骼,是没有厚度的,所以只能算到最高的骨头的坐标位置,而不是透顶。
不过问题不大,因为下面还会用对齐骨骼来修改骨骼位置的,所以我是获取了biped骨骼的Head到Foot的距离,作为高度,等于是给骨骼一个大概的默认值。

4.关于旋转角度和坐标

因为下面还会再次对齐骨骼,所以位置和旋转都不重要,先随便给个默认值就行。

5.骨骼的存储

由于工具已经分成了biped部位和自定义骨骼的列表,所以我分别用了2个Struct结构体来存储biped和自定义骨骼的数据结构。

2、生成新的Biped

通过了上面的数据收集,我们已经得到了创建Biped所必须的参数了。
通过biped.createNew方法,就可以把Biped创建出来了。

local bipedHeight = maxHeight - minHeight
	newBipedObj = biped.createNew bipedHeight -90 [0,0,0] \
	arms:needArm \
	neckLinks:neckNum \ 
	spineLinks:spineNum \
	legLinks:legNum \
	tailLinks:tailNum \
	ponyTail1Links:ponyTail1Num \ 
	ponyTail2Links:ponyTail2Num \
	fingers:fingerNum \
	fingerLinks:fingerLinkNum \
	toes:toeNum \ 
	toeLinks:toeLinkNum \
	trianglePelvis:needTrianglePelvis \ 
	triangleNeck:needTriangleNeck \	
	prop1Exists:needProps1 \
	prop2Exists:needProps2 \
	prop3Exists:needProps3
	
	newBipedObj.controller.rootName = bipedName

有2点值得注意的地方:
1.想要整个Biped一起改名,比如从Bip001改成Bip002,并不是遍历所有骨骼逐个对象去改变name属性,而是通过获得Biped的controller,然后修改rootName,这样整个Biped所有节点的名字都会跟着一起变化。
2.这一个步骤创建的只是Biped的部分。实际上很多角色模型会在Biped的基础上再添加了一些bone类型的骨骼作为细节,这些bone骨骼在这一步并没有生成出。由于这些骨骼的位置是跟随着正常的Biped骨骼的,所以我们会在生成完Biped骨骼之后再生成。

3、对齐骨骼

这个步骤看着似乎很简单,不就是每个骨骼找到之前原始骨骼的同名字骨骼,然后复制一下位移旋转缩放信息吗?
实际上并不是这样的,这是因为:
1.Biped里面,有些骨骼是可以直接移动,有些骨骼是依靠父子连接关系和IK来移动的。所以如果对骨骼直接设置位置坐标,很多骨骼都不能生效的。能直接设置位置坐标的骨骼反而不多,只有根骨骼、颈部、左右肩膀、第一根Spine、手指和脚趾的第一关节。
2.导入后的bone骨骼,缩放都是1的,但Biped的骨骼缩放并不是1,不能按照bone的设置。
这样看来,除了旋转信息,好像也就没什么信息可以直接设置了。
不过问题并不是没法解决的。实际上上面2个问题是同一个问题。如果我们要手动的去适配骨骼的位置,正常的做法是,先进入Figure Mode,然后通过缩放,把父级骨骼放大缩小到合适的大小,已达到调整子骨骼位置的目的。如果用脚本来操作,其实也是一样的,只需要计算骨骼的缩放就行了。
计算方式是,过去父级骨骼的坐标和子骨骼的坐标,计算距离,这个距离就是父级骨骼的缩放值了。
不过并不是所有骨骼都是这样计算缩放,这里分成几种情况:
1.根骨骼不需要缩放
2.盆骨Pelvis不论它的父子结构怎样,它的缩放都是Pelvis离其中一根大腿Thigh的距离再乘以2
3.胸骨Spine的最后一根骨骼不需要设置缩放,因为它的子级骨骼是可以直接设置坐标,不需要靠父级的缩放来定位置。其他的子级可以直接设置坐标的骨骼也同理。
这里还需要注意一个地方,Biped物体是不能直接获取pos之类的信息的,必须先获取transform,再获取pos、rotation、scale.

4、获取蒙皮信息

对于一个Skin蒙皮的模型来说,需要先获取它的蒙皮骨骼列表。
这里有个需要注意的地方,Skin里面有2个骨骼的列表,一个是根据UI显示列表的顺序来排序,另外一个是蒙皮计算时的实际骨骼Id,我们要注意,需要获取的是实际骨骼Id,而不是UI显示排序的id。
所以我们要先通过skinOps.GetNumberBones方法获取蒙皮骨骼列表里面的数量,再通过skinOps.GetBoneName方法获取某个骨骼id的骨骼的名称,然后存起来。
接下来要逐个顶点获取信息
一个顶点蒙皮信息里面有2个信息需要获取:
1.一个顶点收到了多少根骨骼影响
2.一个顶点收到每根骨骼影响的权重是多少。
这里我也是使用Struct结构体去记录一个蒙皮网格的蒙皮骨骼列表和顶点蒙皮权重信息。

5、重新蒙皮

由于有了上一步的蒙皮信息,所以复制原有的蒙皮网格,然后添加Skin修改器,再把蒙皮信息赋值到新的蒙皮网格里面就可以了。

四、完整代码:

(
--ui
local HelpWin
local BoneListWin
local CustomBoneListWin
local BoneCtrlWin
local SkinCtrlWin

--function
local GetMatch
local AddToBoneDict
local GetBoneInfoByName
local InitBoneList
local FitBoneToBiped
local GetFitName
local CreateBipedFun
local GetBipedHeight
local GetCommonBoneNum
local GetNeckNum
local GetSpineNum
local GetLegNum
local GetTailNum
local GetPonytail1Num
local GetPonytail2Num
local GetFingerNum
local GetFingerLinkNum
local GetToeNum
local GetProp
local CheckNeedArm
local CheckNeedTrianglePelvis
local CheckNeedTriangleNeck
local GetBipedName
local AddToCustomBoneDict
local GetCustomBoneInfoByName
local CheckCustomBone
local CheckOneCustomBone
local CleanCustomBone
local UpdateCustomBoneList
local CheckBoneIsBiped
local CheckNewBipedObj
local FitBoneToOrig
local CreateCustomBone
local ResizeNewBipedFun
local ResizeBoneFun
local RePositionBoneFun
local ResizeOneBone
local SaveBoneListFun
local LoadBoneListFun
local GetSaveBoneListPathFun
local GetLoadBoneListPathFun
local LoadBoneListFitOneBone
local GetSubBoneByName
local AddCustomBoneToList
local AddOneCustomBoneToList
local CheckSelectionIsBipedRoot
local CheckSelectSingleObj
local AddToSkinMeshList
local AddOneObjToSkinMeshList
local RemoveSkinMeshByName
local UpdateSkinMeshList
local SkinFun
local SkinOneMesh
local GetNewBoneByName
local InitNewBipedBoneList
local AddToNewBipedBoneList
local GetNewBipedBoneByName
--var
local mustHaveBoneList = #("root","Pelvis","Spine","L Thigh","L Calf","L Foot",\
	"L Toe0","R Thigh","R Calf","R Foot","R Toe0","Neck","L Clavicle","L UpperArm",\
	"L Forearm","L Hand","L Finger0","R Clavicle","R UpperArm","R Forearm","R Hand",\
	"R Finger0","Head")
local maxBoneList = #("root","Prop1","Prop2","Prop3","Pelvis","Spine","Spine1","Spine2",\
	"Spine3","Spine4","Spine5","Spine6","Spine7","Spine8","Spine9","Neck","Neck1","Neck2",\
	"Neck3","Neck4","Neck5","Neck6","Neck7","Neck8","Neck9","Neck10","Neck11","Neck12","Neck13",\
	"Neck14","Neck15","Neck16","Neck17","Neck18","Neck19","Neck20","Neck21","Neck22","Neck23",\
	"Neck24","Head","Ponytail1","Ponytail11","Ponytail12","Ponytail13","Ponytail14","Ponytail15",\
	"Ponytail16","Ponytail17","Ponytail18","Ponytail19","Ponytail110","Ponytail111","Ponytail112",\
	"Ponytail113","Ponytail114","Ponytail115","Ponytail116","Ponytail117","Ponytail118","Ponytail119",\
	"Ponytail120","Ponytail121","Ponytail122","Ponytail123","Ponytail124","Ponytail2","Ponytail21",\
	"Ponytail22","Ponytail23","Ponytail24","Ponytail25","Ponytail26","Ponytail27","Ponytail28",\
	"Ponytail29","Ponytail210","Ponytail211","Ponytail212","Ponytail213","Ponytail214","Ponytail215",\
	"Ponytail216","Ponytail217","Ponytail218","Ponytail219","Ponytail220","Ponytail221","Ponytail222",\
	"Ponytail223","Ponytail224","L Clavicle","L UpperArm","L Forearm","L Hand","L Finger0","L Finger01",\
	"L Finger02","L Finger03","L Finger1","L Finger11","L Finger12","L Finger13","L Finger2","L Finger21",\
	"L Finger22","L Finger23","L Finger3","L Finger31","L Finger32","L Finger33","L Finger4","L Finger41",\
	"L Finger42","L Finger43","R Clavicle","R UpperArm","R Forearm","R Hand","R Finger0","R Finger01",\
	"R Finger02","R Finger03","R Finger1","R Finger11","R Finger12","R Finger13","R Finger2","R Finger21",\
	"R Finger22","R Finger23","R Finger3","R Finger31","R Finger32","R Finger33","R Finger4","R Finger41",\
	"R Finger42","R Finger43","L Thigh","L Calf","L HorseLink","L Foot","L Toe0","L Toe01","L Toe02",\
	"L Toe1","L Toe11","L Toe12","L Toe2","L Toe21","L Toe22","L Toe3","L Toe31","L Toe32","L Toe4",\
	"L Toe41","L Toe42","R Thigh","R Calf","R HorseLink","R Foot","R Toe0","R Toe01","R Toe02","R Toe1",\
	"R Toe11","R Toe12","R Toe2","R Toe21","R Toe22","R Toe3","R Toe31","R Toe32","R Toe4","R Toe41",\
	"R Toe42","Tail","Tail1","Tail2","Tail3","Tail4","Tail5","Tail6","Tail7","Tail8","Tail9","Tail10",\
	"Tail11","Tail12","Tail13","Tail14","Tail15","Tail16","Tail17","Tail18","Tail19","Tail20","Tail21",\
	"Tail22","Tail23","Tail24")

local boneInfoDict
local selectedBoneRootObj
local bipedName
local newBipedObj
local customBoneInfoDict
local customBoneNameList
local skinMeshList
local skinInfoList
local newBipedObjNameList
local newBipedObjDict
--struct


	
struct VertexSkinInfo
(
	public
	index,
	boneIdList,
	weightList,
	fn AddWeightInfo id val = 
	(
		if this.weightList == undefined then
		(
			this.weightList = #()
		)
		if this.boneIdList == undefined then
		(
			this.boneIdList = #()
		)
		append this.boneIdList id
		append this.weightList val
	)
	
)

struct MeshSkinInfo
(
	skinObj,
	boneNameList,
	vertexInfoList,
	fn AddVertexInfo info = 
	(
		if vertexInfoList == undefined then
		(
			vertexInfoList = #()
		)
		append vertexInfoList info
	),	
	fn SetData skinMeshObj = 
	(
		if skinMeshObj == undefined then
			return 0
		this.skinObj = skinMeshObj
		select skinMeshObj
		local oldModSkin = skinMeshObj.modifiers[Skin]
		if oldModSkin == undefined then
		(
			print "No Skin on the object"
			return 0
		)
		max modify mode
		modPanel.setCurrentObject oldModSkin
		local boneNum = skinOps.GetNumberBones oldModSkin
		this.boneNameList = #()
		for i in 1 to boneNum do
		(
			local boneName = skinOps.GetBoneName oldModSkin i 1
			append this.boneNameList boneName
		)
		
		local vertexNum = GetNumVerts skinMeshObj.mesh
		this.vertexInfoList = #()
		for i in 1 to vertexNum do
		(
			local oneVertInfo = VertexSkinInfo()
			oneVertInfo.index = i
			local skinBoneNum = skinOps.GetVertexWeightCount oldModSkin i

			for j in 1 to skinBoneNum do
			(
				local tempBoneId = skinOps.GetVertexWeightBoneID  oldModSkin i j
				local tempWeight = skinOps.GetVertexWeight  oldModSkin i j
				oneVertInfo.AddWeightInfo tempBoneId tempWeight
			)
			append this.vertexInfoList oneVertInfo
		)
		
	)
	
)
	
struct CustomBoneInfoObj
(
	public 
	name,
	pos,
	rotation,
	scale,
	selectBone,
	newBone,
	parentName,
	parentIsBiped,
	fn SetData obj = 
	(
		this.selectBone = obj
		this.name = obj.name
		this.pos = obj.transform.pos
		this.rotation = obj.transform.rotation
		this.scale = obj.transform.scale
		if obj.parent != undefined then
		(
			local bipedBoneName = CheckBoneIsBiped obj.parent
			if bipedBoneName != undefined then
			(
				this.parentName = bipedBoneName
				this.parentIsBiped = true
			)
			else
			(
				this.parentName = obj.parent.name
				this.parentIsBiped = false
			)
		)
		else
		(
			this.parentName = undefined
			this.parentIsBiped = false
		)
	),
	fn SetNewBone val = 
	(
		this.newBone = val
	)
)
	
struct BoneInfoObj
(
	public
	name,
	isMust,
	selectBone,
	newBone,
	pos,
	rotation,
	scale,
	row,
	fn GetShowName = 
	(
		return this.name
	),
	fn GetMust = 
	(
		if this.isMust == true then
			return "*"
		else 
			return ""
	),
	fn SetName val = 
	(
		this.name = val
		local index = findItem mustHaveBoneList val
		if index >0 then
		(
			this.isMust = true
		)
		else
		(
			this.isMust = false
		)
	),
	fn SetSelectBone val = 
	(
		this.selectBone = val
		if this.selectBone != undefined then
		(
			this.pos = val.transform.pos
			this.rotation = val.transform.rotation
			this.scale = val.transform.scale
		)		
		else
		(
			this.pos = undefined
			this.rotation = undefined
			this.scale = undefined
		)
		this.UpdateRow()
	),
	fn SetNewBone val = 
	(
		this.newBone = val
	),
	fn SetRow val = 
	(
		this.row = val
	),
	fn UpdateRow = 
	(
		if this.row != undefined then
		(
			local content = "";
			if this.selectBone != undefined then
			(
				content = this.selectBone.name
			)
			this.row.subItems.item[2].text = content
		)
	)
)
	
--tool function
fn GetMatch str1 str2 = 
(
	local regexStr = "*"+str2+"*"
	return matchPattern str1 pattern:regexStr
)

fn GetMatchEnd str1 str2 = 
(
	local regexStr = "*"+str2
	return matchPattern str1 pattern:regexStr
)

fn AddToBoneDict info = 
(
	if boneInfoDict == undefined then
	(
		boneInfoDict = #()
	)
	local index = findItem maxBoneList info.name
	if index> 0 then
	(
		boneInfoDict[index] = info
	)
	
)

fn GetBoneInfoByName val = 
(
	if boneInfoDict == undefined then
	(
		return undefined
	)
	local index = findItem maxBoneList val
	if index > 0 then
		return boneInfoDict[index]
	else
		return undefined
)

fn AddToCustomBoneDict info = 
(
	if customBoneInfoDict == undefined then
	(
		customBoneInfoDict = #()
	)
	if customBoneNameList == undefined then
	(
		customBoneNameList = #()
	)
	local index = findItem customBoneNameList info.name
	if index >0 then
	(
		customBoneInfoDict[index] = info
	)
	else
	(
		append customBoneNameList info.name
		append customBoneInfoDict info
	)
)

fn GetCustomBoneInfoByName val = 
(
	if customBoneNameList == undefined or customBoneInfoDict == undefined then
	(
		return undefined
	)
	local index = findItem customBoneNameList val
	if index > 0 then
	(
		return customBoneInfoDict[index]
	)
	else
	(
		return undefined
	)
	
)

fn RemoveCustomBoneInfoByName val = 
(
	if customBoneNameList == undefined or customBoneInfoDict == undefined then
	(
		return 0
	)
	local index = findItem customBoneNameList val
	if index < 0 then
	(
		return 0
	)
	deleteItem customBoneNameList index
	deleteItem customBoneInfoDict index
	UpdateCustomBoneList()
)

fn CheckBoneIsBiped obj = 
(
	
	local curName = undefined
	for i in 1 to boneInfoDict.count do
	(
		local info = boneInfoDict[i]
		if info.selectBone != undefined and info.selectBone == obj then
		(
			curName = info.name
			break
		)
	)
	return curName
)

--ui function
fn InitBoneList = 
(	
	boneInfoDict = #()
	for i in 1 to maxBoneList.count do
	(
		local info = BoneInfoObj()
		info.SetName maxBoneList[i]
		AddToBoneDict info
	)
)

fn ShowMsg msg = 
(
	MessageBox msg title:"Tips"
)

fn GetFitBoneInfo val = 
(
	
	local index = 0
	local newName = val
	if newName != "root" then
		newName = replace val 1 (bipedName.count+1) ""
	for i in 1 to maxBoneList.count do
	(
		if newName == maxBoneList[i] then
		(
			index = i
			break
		)
	)
	if index >0 then
	(
		return boneInfoDict[index]
	)
	else
	(
		return undefined
	)
	
)

fn FitBoneToBiped obj =
(
	local objName = obj.name
	if objName == bipedName then
	(
		objName = "root"
	)
	local info = GetFitBoneInfo objName
	if info != undefined then
		info.SetSelectBone obj
	
	local childrenList = obj.children
	if childrenList != undefined and childrenList.count >0 then
	(
		for i in 1 to childrenList.count do
		(
			FitBoneToBiped childrenList[i]
		)
	)
)


fn FitBoneToOrig obj =
(
	local objName = obj.name
	if objName == bipedName then
	(
		objName = "root"
	)
	local boneType = classof obj
	if boneType!= Dummy and (GetMatch objName "Footstep") == false then
	(
		if boneType == Biped_Object then
		(
			local info = GetFitBoneInfo objName
			if info != undefined then
				info.SetNewBone obj
		)
		else
		(
			local info = GetCustomBoneInfoByName objName
			if info != undefined then
				info.SetNewBone obj
		)
	)
	local childrenList = obj.children
	if childrenList != undefined and childrenList.count >0 then
	(
		for i in 1 to childrenList.count do
		(
			FitBoneToOrig childrenList[i]
		)
	)
)


fn CheckMustBone = 
(
	for item in boneInfoDict do
	(
		if item.isMust == true and item.selectBone == undefined then
		(
			return item.name
		)
	)
	return undefined
)


fn GetBipedHeight = 
(
	local headInfo = GetBoneInfoByName("Head")
	local footInfo = GetBoneInfoByName("L Foot")
	return (distance headInfo.pos footInfo.pos)
)

fn GetCommonBoneNum name1 name2 = 
(
	local indexStart = findItem maxBoneList name1
	local indexEnd = findItem maxBoneList name2
	local num = 0;
	for i in indexStart to indexEnd do
	(
		local curName = maxBoneList[i]
		local info = GetBoneInfoByName(curName)
		if info != undefined and info.selectBone != undefined then
		(
			num = num +1
		)
	)
	return num
)


fn GetNeckNum = 
(
	return GetCommonBoneNum "Neck" "Neck24"
)

fn GetSpineNum = 
(
	return GetCommonBoneNum "Spine" "Spine9"
)

fn GetLegNum = 
(
	local num = 3
	local info = GetBoneInfoByName "L HorseLink"
	if info != undefined and info.selectBone!=undefined then
	(
		num = 4
	)
	return num
)

fn GetTailNum = 
(
	return GetCommonBoneNum "Tail" "Tail24"
)

fn GetPonytail1Num = 
(
	return GetCommonBoneNum "Ponytail1" "Ponytail124"
)

fn GetPonytail2Num = 
(
	return GetCommonBoneNum "Ponytail2" "Ponytail224"
)

fn GetFingerNum = 
(
	local info = GetBoneInfoByName "L Finger4"
	if info != undefined and info.selectBone != undefined then
	(
		return 5
	)
	
	info = GetBoneInfoByName "L Finger3"
	if info != undefined and info.selectBone != undefined then
	(
		return 4
	)
	
	info = GetBoneInfoByName "L Finger2"
	if info != undefined and info.selectBone != undefined then
	(
		return 3
	)
	
	info = GetBoneInfoByName "L Finger1"
	if info != undefined and info.selectBone != undefined then
	(
		return 2
	)
	
	info = GetBoneInfoByName "L Finger0"
	if info != undefined and info.selectBone != undefined then
	(
		return 1
	)
	
	return 0
)

fn GetFingerLinkNum = 
(
	return GetCommonBoneNum "L Finger0" "L Finger03"
)

fn GetToeNum = 
(
	local info = GetBoneInfoByName "L Toe4"
	if info != undefined and info.selectBone != undefined then
	(
		return 5
	)
	
	info = GetBoneInfoByName "L Toe3"
	if info != undefined and info.selectBone != undefined then
	(
		return 4
	)
	
	info = GetBoneInfoByName "L Toe2"
	if info != undefined and info.selectBone != undefined then
	(
		return 3
	)
	
	info = GetBoneInfoByName "L Toe1"
	if info != undefined and info.selectBone != undefined then
	(
		return 2
	)
	
	info = GetBoneInfoByName "L Toe0"
	if info != undefined and info.selectBone != undefined then
	(
		return 1
	)
	
	return 0
)

fn GetToeLinkNum = 
(
	return GetCommonBoneNum "L Toe0" "L Toe02"
)

fn GetProp val = 
(
	if val == 1 then
	(
		info = GetBoneInfoByName "Prop1"
		if info != undefined and info.selectBone != undefined then
		(
			return true
		)
		else
		(
			return false
		)
	)
	else if val == 2 then
	(
		info = GetBoneInfoByName "Prop2"
		if info != undefined and info.selectBone != undefined then
		(
			return true
		)
		else
		(
			return false
		)
	)
	else if val == 3 then
	(
		info = GetBoneInfoByName "Prop3"
		if info != undefined and info.selectBone != undefined then
		(
			return true
		)
		else
		(
			return false
		)
	)
	else
	(
		return false
	)

)

fn CheckNeedArm = 
(
	info = GetBoneInfoByName "L UpperArm"
	if info != undefined and info.selectBone != undefined then
	(
		return true
	)
	else
	(
		return false
	)
)

fn CheckNeedTrianglePelvis =
(
	local result = false;
	local thighInfo = GetBoneInfoByName "L Thigh"
	local spineInfo = GetBoneInfoByName "Spine"
	if thighInfo!= undefined and thighInfo.selectBone != undefined then
	(
		if spineInfo!= undefined and spineInfo.selectBone != undefined then
		(
			if thighInfo.selectBone.parent == spineInfo.selectBone then
			(
				result = true
			)
		)
	)
	return result
)

fn CheckNeedTriangleNeck = 
(
	local result = true;
	local clavicleInfo = GetBoneInfoByName "L Clavicle"
	local neckInfo = GetBoneInfoByName "Neck"
	if clavicleInfo!= undefined and clavicleInfo.selectBone != undefined then
	(
		if neckInfo!= undefined and neckInfo.selectBone != undefined then
		(
			if clavicleInfo.selectBone.parent == neckInfo.selectBone then
			(
				result = false
			)
		)
	)
	return result
)

fn CreateBipedFun = 
(
	local missingName = CheckMustBone()
	if missingName != undefined then
	(
		ShowMsg("MustNeed Bone Missing:"+missingName)
		return 0
	)
	local bipedHeight = GetBipedHeight()
	local numNum = GetNeckNum()
	local neckNum = GetNeckNum()
	local spineNum = GetSpineNum()
	local legNum = GetLegNum()
	local tailNum = GetTailNum()
	local ponytail1Num = GetPonytail1Num()
	local ponytail2Num = GetPonytail2Num()
	local fingerNum = GetFingerNum()
	local fingerLinkNum = GetFingerLinkNum()
	local toeNum = GetToeNum()
	local toeLinkNum = GetToeLinkNum()
	local needProp1 = GetProp 1
	local needProp2 = GetProp 2
	local needProp3 = GetProp 3
	local needArm = CheckNeedArm()
	local needTrianglePelvis = CheckNeedTrianglePelvis()
	local needTriangleNeck = CheckNeedTriangleNeck()
	
	newBipedObj = biped.createNew bipedHeight -90 [0,0,0] \
	arms:needArm \
	neckLinks:neckNum \ 
	spineLinks:spineNum \
	legLinks:legNum \
	tailLinks:tailNum \
	ponyTail1Links:ponytail1Num \ 
	ponyTail2Links:ponytail2Num \
	fingers:fingerNum \
	fingerLinks:fingerLinkNum \
	toes:toeNum \ 
	toeLinks:toeLinkNum \
	trianglePelvis:needTrianglePelvis \ 
	triangleNeck:needTriangleNeck \	
	prop1Exists:needProp1 \
	prop2Exists:needProp2 \
	prop3Exists:needProp3
	
	newBipedObj.controller.rootName = GetBipedName()	
	FitBoneToOrig newBipedObj
)

fn SetBipedName val = 
(
	bipedName = val
	BoneCtrlWin.bipedNameTxt.text = val
)

fn GetBipedName = 
(
	return BoneCtrlWin.bipedNameTxt.text
)

fn CheckCustomBone = 
(
	if selectedBoneRootObj == undefined then
	(
		ShowMsg("Please select boneRootFirst!")
		return 0
	)
	customBoneInfoDict = #()
	customBoneNameList = #()
	CheckOneCustomBone selectedBoneRootObj
	UpdateCustomBoneList()
)

fn CheckOneCustomBone obj =
(
	if (classof obj)!= Dummy and (GetMatch obj.name "Footstep") == false and (GetMatchEnd obj.name "Nub") == false then
	(
		local bipedBoneName = CheckBoneIsBiped obj
		if bipedBoneName == undefined then
		(
			local info = CustomBoneInfoObj()
			info.SetData obj
			AddToCustomBoneDict info
		)
	)
	local childrenList = obj.children
	if childrenList != undefined and childrenList.count >0 then
	(
		for i in 1 to childrenList.count do
		(
			CheckOneCustomBone childrenList[i]
		)
	)
)	

fn CleanCustomBone = 
(
	customBoneInfoDict = #()
	customBoneNameList = #()
	UpdateCustomBoneList()
)

fn UpdateCustomBoneList = 
(
		CustomBoneListWin.boneDataList.Clear()
		CustomBoneListWin.boneDataList.Fullrowselect = true
		CustomBoneListWin.boneDataList.GridLines = true
	    CustomBoneListWin.boneDataList.View = CustomBoneListWin.boneDataList.View.Details
	    
		if customBoneInfoDict == undefined or customBoneInfoDict.count <=0 then
		(
			return 0
		)
	    CustomBoneListWin.boneDataList.Columns.Add "BoneName" 100
		CustomBoneListWin.boneDataList.Columns.Add "parentType" 100
	    CustomBoneListWin.boneDataList.Columns.Add "parentName" 200
	    

		for i = 1 to customBoneInfoDict.count do
		(
			local info = customBoneInfoDict[i]
			row = dotNetObject "System.Windows.Forms.ListViewItem"
	        row.Text = info.name
			local parentType = ""
			local parentName = ""
			if info.parentName != undefined then
			(
				parentName = info.parentName
				if info.parentIsBiped == true then
				(
					parentType = "biped"
				)
				else
				(
					parentType = "bone"
				)
			)
	        row.SubItems.Add(parentType)
			row.SubItems.Add(parentName)
	        CustomBoneListWin.boneDataList.Items.Add(row)
		)
)

fn CheckNewBipedObj = 
(
	if newBipedObj == undefined then
	(
		ShowMsg "Please create biped or select biped first"
		return 0
	)
	FitBoneToOrig newBipedObj
)

fn CreateCustomBone = 
(
	if customBoneInfoDict == undefined and customBoneInfoDict.count == 0 then
	(
		return 0
	)
	for i in 1 to customBoneInfoDict.count do
	(
		local info = customBoneInfoDict[i]
		if info.selectBone != undefined then
		(
			local newBone = copy info.selectBone
			newBone.name = info.selectBone.name
			info.SetNewBone newBone
			if info.parentName != undefined then
			(
				if info.parentIsBiped == true then
				(
					local parentInfo = GetBoneInfoByName info.parentName
					if parentInfo != undefined and parentInfo.newBone != undefined then
					(
						newBone.parent = parentInfo.newBone
					)
				)
				else
				(
					local parentInfo = GetCustomBoneInfoByName info.parentName
					if parentInfo != undefined and parentInfo.newBone != undefined then
					(
						newBone.parent = parentInfo.newBone
					)
				)
			)
		)
	)
)

fn ResizeNewBipedFun = 
(
	if newBipedObj == undefined then
	(
		ShowMsg "Please create biped or select biped first"
		return 0
	)
	newBipedObj.controller.figureMode = true
	ResizeBoneFun newBipedObj
	RePositionBoneFun()
	newBipedObj.controller.figureMode = false
)

fn ResizeBoneFun obj = 
(
	local objName = obj.name
	if objName == bipedName then
	(
		objName = "root"
	)
	local boneType = classof obj
	if boneType!= Dummy and (GetMatch objName "Footstep") == false then
	(
		if boneType == Biped_Object then
		(
			local info = GetFitBoneInfo objName
			ResizeOneBone info
		)

	)
	local childrenList = obj.children
	if childrenList != undefined and childrenList.count >0 then
	(
		for i in 1 to childrenList.count do
		(
			ResizeBoneFun childrenList[i]
		)
	)
)

fn ResizeOneBone info = 
(
	if info.selectBone == undefined or info.newBone == undefined then
		return 0
	local childrenList =info.selectBone.children
	if childrenList == undefined or childrenList.count ==0 then
	(
		return 0
	)
	if info.name == "root" then
		return 0
	if info.name == "L Foot" or info.name == "R Foot" then
		return 0
	if info.name == "Pelvis" then
	(
		local nextInfo = GetBoneInfoByName "L Thigh"
		if nextInfo != undefined then
		(
			local tempScale = distance info.pos nextInfo.pos
			tempScale = tempScale*2
			biped.setTransform info.newBone #scale [tempScale,tempScale,tempScale] true
		)
	)
	else
	(
		if (GetMatch info.name "Spine") == true then
		(
			if (GetMatch childrenList[1].name "Spine") == false then
			(
				return 0
			)
		)
		local maxVal = 0
		for i in 1 to childrenList.count do
		(
			local tempVal = distance info.pos childrenList[i].transform.pos
			if tempVal >maxVal then
				maxVal = tempVal
		)
		if maxVal > 0 then
		(
			biped.setTransform info.newBone #scale [maxVal,maxVal,maxVal] true
		)
	)
	
)

fn RePositionBoneFun = 
(
	if boneInfoDict != undefined and boneInfoDict.count >0 then
	(
		for i in 1 to boneInfoDict.count do
		(
			local info = boneInfoDict[i]
			if info.selectBone != undefined and info.newBone != undefined then
			(
				biped.setTransform info.newBone #pos info.pos true
				biped.setTransform info.newBone #rotation info.rotation true
				biped.setTransform info.newBone #pos info.pos true
				biped.setTransform info.newBone #rotation info.rotation true
			)
		)
	)
	
	if customBoneInfoDict != undefined and customBoneInfoDict.count >0 then
	(
		for i in 1 to customBoneInfoDict.count do
		(
			local info = customBoneInfoDict[i]
			if info.selectBone != undefined and info.newBone != undefined then
			(
				info.newBone.pos = info.selectBone.pos
				info.newBone.rotation = info.selectBone.rotation
				info.newBone.scale = info.selectBone.scale
			)
		)
	)
)

fn GetSaveBoneListPathFun = 
(
	local savePath = getSaveFileName types:"csv(*.csv)|*csv"
	if savePath == undefined then
	(
		return undefined
	)
	if (matchPattern savePath pattern:"*.csv") == false then
	(
		savePath+=".csv"
	)
	return savePath
)

fn SaveBoneListFun = 
(
	if boneInfoDict == undefined or boneInfoDict.count == 0 then
	(
		ShowMsg("No data can save!")
		return 0
	)
	local savePath = GetSaveBoneListPathFun()
	if savePath == undefined then
	(
		return 0
	)
	local content = "";
	for i in 1 to boneInfoDict.count do
	(
		local info = boneInfoDict[i]
		if info.selectBone != undefined then
		(
			content+=info.name+","+info.selectBone.name
			content += "\n"
		)
	)
	if content == "" then
	(
		ShowMsg("No bone can save,Please assign or autofit bone first!")
		return 0
	)
	local f = createFile savePath
	format content to:f
	close f
	ShowMsg ("save file to :"+savePath)
	
)

fn GetLoadBoneListPathFun = 
(
	local loadPath = getOpenFileName types:"csv(*.csv)|*csv"
	if loadPath == undefined then
	(
		return undefined
	)

	return loadPath
)

fn LoadBoneListFun = 
(
	if selectedBoneRootObj == undefined then
	(
		ShowMsg "Please select boneRoot"
		return 0
	)

	local loadPath = GetLoadBoneListPathFun()
	if loadPath == undefined then
	(
		return 0
	)
	local f = openFile loadPath
	local lineList = #()
	while not eof f do
	(
		local lineStr = readLine f
		append lineList lineStr
	)
	close f
	for i in 1 to lineList.count do
	(
		LoadBoneListFitOneBone lineList[i]
	)
)

fn LoadBoneListFitOneBone val = 
(
	if selectedBoneRootObj == undefined then
		return 0
	local strArr = filterString val ","
	if strArr == undefined or strArr.count < 2 then
	(
		return 0
	)
	local key = strArr[1]
	local boneName = strArr[2]
	local info = GetBoneInfoByName key
	if info == undefined then
	(
		return 0
	)
	local targetBoneObj = GetSubBoneByName selectedBoneRootObj boneName
	if targetBoneObj != undefined then
	(
		info.SetSelectBone targetBoneObj
	)
)

fn GetSubBoneByName obj val = 
(
	if obj.name == val then
		return obj
	
	local resultObj = undefined
	local childrenList = obj.children
	if childrenList != undefined and childrenList.count > 0 then
	(
		for i in 1 to childrenList.count do
		(
			local subResult = GetSubBoneByName childrenList[i] val
			if subResult != undefined then
			(
				resultObj = subResult
				break
			)
		)
	)
	return resultObj
)

fn AddCustomBoneToList = 
(
	if $ == undefined then
		return 0
	local tempList = #()
	local tempType = classof $
	if tempType == ObjectSet then
	(
		if $.count > 0 then
		(
			for i in 1 to $.count do
			(
				append tempList $[i]
			)
		)
	)
	else
	(
		append tempList $
	)
	if tempList.count == 0 then
		return 0
	
	for i in 1 to tempList.count do
	(
		AddOneCustomBoneToList tempList[i]
	)
	UpdateCustomBoneList()
	
)

fn AddOneCustomBoneToList obj = 
(
	local objType = classOf obj
	if objType == Dummy or objType == Biped_Object then
		return 0
	if customBoneNameList != undefined then
	(
		local index = findItem customBoneNameList obj.name
		if index >0 then
			return 0
	)
	
	local info = CustomBoneInfoObj()
	info.SetData obj
	AddToCustomBoneDict(info)
	
)

fn CheckSelectionIsBipedRoot obj = 
(
	if obj == undefined then
		return false
	
	local objType = classof obj
	if objType != Biped_Object then
		return false
	
	objType = classof obj.controller
	if objType != Vertical_Horizontal_Turn then
		return false
	
	return true
)

fn CheckSelectSingleObj obj = 
(
	if obj == undefined then
		return false
	
	local objType = classof obj
	if objType == ObjectSet or objType == Dummy then 
		return false
	
	return true
)

fn AddToSkinMeshList obj = 
(
	if obj == undefined then
		return 0
	
	if skinMeshList == undefined then
		skinMeshList = #()
	
	local objType = classof obj
	local addList = #()
	if objType == ObjectSet then
	(
		for i in 1 to obj.count do
		(
			append addList obj[i]
		)
	)
	else
	(
		append addList obj
	)
	local realAddList = #()
	for i in 1 to addList.count do
	(
		local subObj = addList[i]
		if subObj.modifiers[Skin] != undefined then
		(
			append realAddList subObj
		)
	)
	if realAddList.count == 0 then
	(
		ShowMsg "No Skin on selected objs!"
		return 0
	)
	
	for i in 1 to realAddList.count do
	(
		AddOneObjToSkinMeshList realAddList[i]
	)
	
	UpdateSkinMeshList()
)

fn AddOneObjToSkinMeshList obj = 
(
	local isExist = false
	if skinMeshList.count > 0 then
	(
		for i in 1 to skinMeshList.count do
		(
			if skinMeshList[i] == obj then
			(
				isExist = true
				break
			)
		)
	)
	if isExist == true then
		return 0
	append skinMeshList obj
)

fn RemoveSkinMeshByName val = 
(
	if skinMeshList == undefined or skinMeshList.count == 0 then
		return 0
	
	local index = 0
	for i in 1 to skinMeshList.count do
	(
		if skinMeshList[i].name == val then
		(
			index = i
			break
		)
	)
	if index == 0 then
		return 0
	
	deleteItem skinMeshList index
	UpdateSkinMeshList()
)

fn UpdateSkinMeshList = 
(
	SkinCtrlWin.skinDataList.Items.clear()
	if skinMeshList == undefined or skinMeshList.count ==0 then
		return 0
	
	for i in 1 to skinMeshList.count do
	(
		SkinCtrlWin.skinDataList.Items.Add(skinMeshList[i].name)
	)
)


fn InitNewBipedBoneList = 
(
	newBipedObjNameList = #()
	newBipedObjDict = #()
	if boneInfoDict == undefined or boneInfoDict.count == 0 then
		return 0
	
	for i in 1 to boneInfoDict.count do
	(
		local info = boneInfoDict[i]
		if info.newBone != undefined then
			AddToNewBipedBoneList info.selectBone.name info.newBone
	)
)

fn AddToNewBipedBoneList val obj = 
(
	if newBipedObjNameList == undefined then
		newBipedObjNameList = #()
	
	if newBipedObjDict == undefined then
		newBipedObjDict = #()
	
	append newBipedObjNameList val
	append newBipedObjDict obj
)

fn GetNewBipedBoneByName val = 
(
	if newBipedObjNameList == undefined or newBipedObjDict == undefined then
		return undefined 
	
	local index = findItem newBipedObjNameList val
	if index == 0 then
		return undefined
	
	return newBipedObjDict[index]
)

fn SkinFun = 
(
	if skinMeshList == undefined or skinMeshList.count == 0 then
	(
		ShowMsg "Please add skinMesh first!"
		return 0
	)
	InitNewBipedBoneList()
	skinInfoList = #()
	for i in 1 to skinMeshList.count do
	(
		local info = MeshSkinInfo()
		info.SetData skinMeshList[i]
		append skinInfoList info
	)
	for i in 1 to skinInfoList.count do
	(
		SkinOneMesh skinInfoList[i]
	)

)

fn SkinOneMesh info = 
(
	local newSkinMeshObj = copy info.skinObj
	deleteModifier newSkinMeshObj 1
	select newSkinMeshObj
	ModPanel.addModToSelection(Skin()) ui:on
	local modSkin = newSkinMeshObj.modifiers[Skin]
	for i in  1 to info.boneNameList.count do
	(
		local boneNode = GetNewBoneByName info.boneNameList[i]
		if boneNode != undefined then
		(
			skinOps.addbone modSkin boneNode -1
		)
	)
	vertexNum = GetNumVerts newSkinMeshObj.mesh
	for i in 1 to vertexNum do
	(
		--print i
		skinOps.SetVertexWeights modSkin i info.vertexInfoList[i].boneIdList info.vertexInfoList[i].weightList
	)
)

fn GetNewBoneByName val = 
(
	local info = GetCustomBoneInfoByName val 
	if info != undefined then
		return info.newBone
	

	local bipedBoneObj = GetNewBipedBoneByName val
	if bipedBoneObj != undefined then
		return bipedBoneObj
	
	return undefined
)
	

--ui


--HelpWin
rollout HelpWin "About" width:400 height:212
(
	label 'lbl1' "BoneToBipedTool" pos:[23,10] width:89 height:21 align:#left
	label 'lbl37' "author:Li Weizhao" pos:[23,32] width:157 height:21 align:#left
	HyperLink 'theHyperlink' "https://blog.csdn.net/liweizhao" pos:[54,57] width:309 height:21 address:"https://blog.csdn.net/liweizhao" align:#left
	--label 'lbl38' "blog:https://blog.csdn.net/liweizhao" pos:[23,54] width:352 height:21 align:#left
	label 'lbl39' "Ver 1.0.0" pos:[139,10] width:65 height:16 align:#left
	label 'lbl40' "Step:" pos:[8,82] width:157 height:21 align:#left
	label 'lbl41' "1.assign the original biped part bones" pos:[22,108] width:220 height:21 align:#left
	label 'lbl42' "2.assign the original non-biped custom bones" pos:[22,130] width:220 height:21 align:#left
	label 'lbl43' "3.Create biped and custom bones" pos:[22,151] width:220 height:21 align:#left
	label 'lbl44' "4.select skinMeshes and reskin" pos:[22,172] width:220 height:21 align:#left
	label 'lbl62' "Blog:" pos:[22,56] width:30 height:15 align:#left
)


--boneListWin
rollout BoneListWin "1.Assign BoneList" width:400 height:351
(
	label 'lbl1' "BoneList" pos:[11,11] width:157 height:20 align:#left
	dotNetControl 'boneDataList' "System.Windows.Forms.ListView" pos:[9,30] width:380 height:200 align:#left
	label 'lbl4' "AutoFit:" pos:[39,284] width:77 height:14 align:#left
	label 'lbl5' "original boneRoot:" pos:[37,248] width:97 height:15 align:#left
	label 'selectRootTxt' "UnSelected" pos:[145,249] width:86 height:19 align:#left
	button 'selectAndFitBtn' "SelectAndFit" pos:[149,276] width:159 height:24 align:#left
    
	button 'saveBtn' "Save" pos:[151,308] width:72 height:24 align:#left
	
	
	button 'loadBtn' "Load" pos:[239,308] width:72 height:24 align:#left
	label 'lbl9' "Save And Load:" pos:[38,311] width:115 height:14 align:#left
	button 'selectBtn' "Select" pos:[239,244] width:72 height:24 align:#left
	on BoneListWin open do
	(
	
		boneDataList.Fullrowselect = true
		boneDataList.GridLines = true
	    boneDataList.View = boneDataList.View.Details
	    
	
	    boneDataList.Columns.Add "BoneName" 100
		boneDataList.Columns.Add "MustNeed" 60
	    boneDataList.Columns.Add "SetBone" 200
	    
		InitBoneList()
		for i = 1 to boneInfoDict.count do
		(
			row = dotNetObject "System.Windows.Forms.ListViewItem"
			boneInfoDict[i].SetRow row
	        row.Text = boneInfoDict[i].GetShowName()
	        row.SubItems.Add(boneInfoDict[i].GetMust())
			row.SubItems.Add("")
	        boneDataList.Items.Add(row)
		)
	
	)
	on boneDataList mouseDown arg do
	(
		hit=(boneDataList.HitTest (dotNetObject "System.Drawing.Point" arg.x arg.y))
		local clickName = hit.item.subItems.item[0].text
		local info = GetBoneInfoByName clickName
		if info != undefined then
		(
			if arg.Clicks  == 2 do 
			(
				info.SetSelectBone undefined				
			)
		
			if arg.Clicks  == 1 do 
			(
				info.SetSelectBone $
			)
		)
	)
	on selectAndFitBtn pressed do
	(

		if $ == undefined then
		(
			selectRootTxt.text = "UnSelected"
			selectedBoneRootObj = undefined
		)
		else
		(
			
			if (CheckSelectSingleObj $) == false then
			(
				selectRootTxt.text = "UnSelected"
				selectedBoneRootObj = undefined
				ShowMsg "Please select the biped root only!"
				return 0
			)
			selectedBoneRootObj = $
			selectRootTxt.text = selectedBoneRootObj.name
			SetBipedName selectedBoneRootObj.name
			FitBoneToBiped selectedBoneRootObj
		)
	)
	on saveBtn pressed do
	(
		SaveBoneListFun()
	)
	on loadBtn pressed do
	(
		LoadBoneListFun()
	)
	on selectBtn pressed do
	(
		if $ == undefined then
		(
			selectRootTxt.text = "UnSelected"
			selectedBoneRootObj = undefined
		)
		else
		(
			
			if (CheckSelectSingleObj $) == false then
			(
				selectRootTxt.text = "UnSelected"
				selectedBoneRootObj = undefined
				ShowMsg "Please select the biped root only!"
				return 0
			)
			selectedBoneRootObj = $
			selectRootTxt.text = selectedBoneRootObj.name
			SetBipedName selectedBoneRootObj.name
		)
	)
)


--customBoneListWin
rollout CustomBoneListWin "2.Assign CustomBoneList" width:400 height:324
(
	label 'lbl1' "BoneList(not biped bones)" pos:[11,11] width:157 height:20 align:#left
	dotNetControl 'boneDataList' "System.Windows.Forms.ListView" pos:[9,30] width:380 height:200 align:#left
	label 'lbl5' "boneRoot:" pos:[16,244] width:50 height:15 align:#left
	label 'selectRootTxt' "UnSelected" pos:[81,245] width:86 height:19 align:#left
	button 'selectBtn' "Select And Check" pos:[209,239] width:146 height:24 align:#left 
	
	button 'cleanBtn' "Clean" pos:[209,279] width:147 height:24 align:#left
	button 'addBtn' "Select And Add" pos:[29,278] width:146 height:24 align:#left
	on boneDataList mouseDown arg do
	(
		if arg.Clicks  == 2 do 
		(
			hit=(boneDataList.HitTest (dotNetObject "System.Drawing.Point" arg.x arg.y))
			local clickName = hit.item.subItems.item[0].text
			RemoveCustomBoneInfoByName clickName
		)
		
	)
	on selectBtn pressed do
	(
		if (CheckSelectSingleObj $) == false then
		(
			selectRootTxt.text = "UnSelected"
			selectedBoneRootObj = undefined
			ShowMsg "Please select the biped root only!"
			return 0
		)
		
		selectedBoneRootObj = $
		selectRootTxt.text = selectedBoneRootObj.name
		CheckCustomBone()
	)
	on cleanBtn pressed do
	(
		CleanCustomBone()
	)
	on addBtn pressed do
	(
		AddCustomBoneToList()
	)
)

rollout BoneCtrlWin "3.Bone Controller" width:400 height:249
(
	button 'createBipedBtn' "Create Biped Only" pos:[18,118] width:170 height:36 align:#left
	button 'selectBipedBtn' "SelectBiped" pos:[18,174] width:170 height:36 align:#left
	button 'resizeBtn' "ResizeBones" pos:[214,174] width:170 height:36 align:#left
	label 'lbl1' "Biped Name:" pos:[26,32] width:67 height:17 align:#left
	edittext 'bipedNameTxt' "" pos:[113,27] width:186 height:27 align:#left
	button 'createBoneBtn' "Create Bones Only" pos:[214,117] width:170 height:36 align:#left
	button 'onekeyBtn' "One Key Create" pos:[18,72] width:370 height:36 align:#left

	on createBipedBtn pressed do
	(
		CreateBipedFun()
	)
	on selectBipedBtn pressed do
	(
		if (CheckSelectionIsBipedRoot $) == false then
		(
			selectedBoneRootObj = undefined
			ShowMsg "Please select the biped root only!"
			return 0
		)
		newBipedObj = $
		CheckNewBipedObj()
		
	)
	on resizeBtn pressed do
	(
		ResizeNewBipedFun()
	)
	on createBoneBtn pressed do
	(
		CreateCustomBone()
	)
	on onekeyBtn pressed do
	(
		CreateBipedFun()
		CreateCustomBone()
		ResizeNewBipedFun()
		ResizeNewBipedFun()
	)
)
rollout SkinCtrlWin "4.Skin Controller" width:400 height:320
(
	dotNetControl 'skinDataList' "System.Windows.Forms.ListBox" pos:[9,30] width:380 height:164 align:#left
	label 'lbl32' "Skin Mesh List" pos:[13,8] width:92 height:18 align:#left
	button 'addBtn' "Add" pos:[15,213] width:170 height:31 align:#left
	button 'removeBtn' "Remove" pos:[204,213] width:170 height:31 align:#left
	button 'skinBtn' "ReSkin" pos:[16,272] width:358 height:31 align:#left
	
	
	
	on addBtn pressed do
	(
		AddToSkinMeshList $
	)
	on removeBtn pressed do
	(
		if skinDataList.SelectedItem == undefined then
		(
			ShowMsg "Please select the obj to remove in the ListBox!"
			return 0
		)
		RemoveSkinMeshByName (skinDataList.SelectedItem as string)
	)
	on skinBtn pressed do
	(
		SkinFun()
	)
)
local theFloater = newRolloutFloater "Test" 400 600
addRollout HelpWin theFloater rolledup:false
addRollout BoneListWin theFloater rolledup:true
addRollout CustomBoneListWin theFloater rolledup:true
addRollout BoneCtrlWin theFloater rolledup:true
addRollout SkinCtrlWin theFloater rolledup:true

)
	

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/466689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信息安全复习十:Web与电子商务安全

一、章节梗概 1.信息安全的学科内容 2.Web和电子商务安全问题提出 3.安全套接字协议SSL与传输层安全协议TLS 4.安全电子交易(SET)简要介绍 复习&#xff1a; 密码学内容&#xff1a;对称密钥密码、公开密钥密码、报文鉴别 PKI&#xff1a;数字签名、数字证书、信任关系 身份认…

MES管理系统助力企业数字化与实体经济实现“数实融合“

中国企业评估协会日前公布了2022年度“中国新经济500强”的名单。在这些企业中&#xff0c;先进制造业企业的比例超过了半数&#xff0c;尤其是互联网与现代信息技术服务业、新能源产业、新型生活性服务业等&#xff0c;在这些行业中占据了重要地位。由此可以看出&#xff0c;绿…

C/C++每日一练(20230427) 二叉树专场(5)

目录 1. 从中序与后序遍历序列构造二叉树 &#x1f31f;&#x1f31f; 2. 从先序与中序遍历序列构造二叉树 &#x1f31f;&#x1f31f; 3. 二叉树展开为链表 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每…

微信小程序php+python+nodejs+vue 高校工资管理系统

在线公益知识练习及测试系统是随着计算机技术和互联网技术的发展而产生的一种的新的练习及测试模式&#xff0c;与传统的纸质化练习及测试不同&#xff0c;在线系统提高了练习或测试的效率&#xff0c;减少了纸张的浪费&#xff0c;减轻了教师的评卷压力&#xff0c;同时也为参…

易观千帆 | 金融机构如何保证用户体验长期可持续?

易观&#xff1a;用户体验正逐渐成为金融机构的命脉。 数字经济时代的到来&#xff0c;金融机构面临着来自内部和外部的双重压力。一方面&#xff0c;互联网金融企业凭借强大的技术能力以及人才优势&#xff0c;通过互联网运营的模式迅速响应客户需求&#xff0c;吸引了大量用户…

自定义Feign日志

文章目录 开篇定位Feign是怎么打印日志的&#xff1f;自定义FeignLogger实现 开篇 在上一篇Feign打印日志文章中&#xff0c;已经成功打印了FeignClient请求服务的日志信息&#xff0c;但是默认打印的日志太过零散&#xff0c;不是我们想要的。怎么能自定义日志打印的格式和内…

系统分析师《企业信息化战略与实施》高频知识点二

企业信息化战略与实施---系统建模 组织结构是一个企业内部部门的划分以及相互之间的关系&#xff0c;每个企业都有自己的组织结构图&#xff0c;它将企业分成若干部分&#xff0c;标明行政隶属关系。组织结构图是一种树结构&#xff0c;树的分支是根据上下级和行政隶属关系绘制…

二叉树 + 技巧

题目难度备注2471. 逐层排序二叉树所需的最少操作数目1635BFS 置换环 离散化2641. 二叉树的堂兄弟节点 II1677BFS 文章目录 周赛二叉树问题[2471. 逐层排序二叉树所需的最少操作数目](https://leetcode.cn/problems/minimum-number-of-operations-to-sort-a-binary-tree-by-l…

【AI绘画】云服务器部署stable-diffusion-webui保姆级教程

1.背景 之前给大家写过Mac苹果笔记本上部署stable-diffusion-webui的教程&#xff0c;知乎链接&#xff1a; 【奶奶看了也不会】AI绘画 Mac安装stable-diffusion-webui绘制AI妹子保姆级教程 但是安装过程就花了一天的时间&#xff0c;各种问题处理起来真是苦不堪言。。。而且…

如何解决生产缺料问题?

对于一个生产型企业来说&#xff0c;生产缺料是管理中的疑难问题之一。 生产缺料导致的最直接的危害就有两个&#xff1a; 第一就是不能准时交货&#xff0c;降低企业信用&#xff0c;情况严重可能导致根据合同赔款&#xff0c;甚至丢失客户。 第二个危害就是增加了生产成本…

部署LVS-DR 集群及实验

一、LVS-DR工作原理 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09;工作模式&#xff0c;是生产环境中最常用的一种工作模式。 #①LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff1b; #②节点 Directo…

ueditor富文本编辑器上传木马图片或木马文件漏洞

漏洞分析&#xff1a; ueditor插件目录一般都自带index.html文件&#xff08;完整demo文件&#xff09;&#xff0c;这个文件原来是用来测试插件功能的&#xff0c;但粗心的程序员们&#xff0c;开发好项目后&#xff0c;都忘记删除这个demo文件了&#xff0c;导致黑客可以很轻…

微服务不是本地部署的最佳选择,不妨试试模块化单体

微服务仅适用于成熟产品 关于从头开始使用微服务&#xff0c;马丁・福勒&#xff08;Martin Fowler&#xff09;总结道&#xff1a; 1. 几乎所有成功的微服务都是从一个过于庞大而不得不拆分的单体应用开始的。 2. 几乎所有从头开始以微服务构建的系统&#xff0c;最后都会因…

win11安装双系统ubuntu20.04指导

目录 一、制作U盘启动盘二、硬盘分区2.1方法2.2分区过程 三、安装系统3.1进入U盘启动3.2安装ubuntu3.3设置启动项 四、更新软件五、遇到的问题5.1不能连接WIFI 电脑型号&#xff1a;联想拯救者Y7000P 2023 无线网卡型号&#xff1a;WIFI 6E AX211 160MHz 系统版本&#xff1a;w…

借助尾号限行 API 实现限行规则应用的设计思路分析

引言 尾号限行是指根据车牌号的末尾数字&#xff0c;规定某些时段内不能在特定区域行驶&#xff0c;这是城市交通管理的一种措施。尾号限行政策的实施可以缓解城市交通拥堵问题&#xff0c;减少环境污染和交通事故等问题。 尾号限行 API 是一种提供已知所有执行限行政策的城市…

iSulad+Kuasar:管理面资源消耗锐减 99%的新一代统一容器运行时解决方案

随着云计算和容器技术的不断发展&#xff0c;容器引擎和容器运行时已经成为了云原生时代的基石&#xff0c;它们负责了容器生命周期的管理以及容器运行过程中环境的创建和资源的配置。openEuler 社区基于容器引擎项目 iSulad[1]在解决容器运行效率、安全性以及隔离性等问题上进…

DVWD-Command Injection Low/Medium/High低中高级别

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 Command Injection 一、Low级别二、Medium级别三、High级别 命令注入这关是一个Ping测试功…

《港联证券》半导体复苏预期“抢跑”产业现实 细分市场缓慢回温

虽然说没有一个冬季不可逾越&#xff0c;但本轮“抢跑”的半导体复苏预期&#xff0c;不得不继续面对工业缓慢复苏的实际。 自2022年下半年以来&#xff0c;资本商场上半导体复苏接棒“缺芯”成为关注焦点&#xff0c;部分半导体职业个股迅速收复跌幅&#xff0c;刷新阶段高点&…

Apache ActiveMQ 远程代码执行漏洞 (CVE-2016-3088)复现

当前漏洞环境部署在vulhub,当前验证环境为vulhub靶场&#xff08;所有实验均为虚拟环境&#xff09; 实验环境&#xff1a;攻击机----kali 靶机&#xff1a;centos7 1、进入环境cd activemq/CVE-2016-3088/&#xff08;靶机&#xff09; 2、启动环境&#xff1a;docker-compos…

设备健康管理软件如何帮助企业优化设备维保计划?

基于AI和工业互联网技术的新型设备管理系统&#xff0c;可以通过实时监测设备运行状态、预测潜在故障、提供预防性维护建议等方式&#xff0c;实现设备管理的数字化和智能化。该类设备管理系统的核心功能一般包括设备状态监测、故障预测、预防性维护、故障知识库管理等&#xf…