如果一切正常,下面我们来继续编写我们的工具。我们的工具需要指明我们需要Bake的动画帧范围,比如0到30帧的动画范围我们需要把它bake到我们的贴图里。所以我们需要一个指认动画范围的UI。同时我们的工具还需要动画的采样密度,0~30帧这个范围我们是把每帧都记录下来还是每隔一帧记录一次。然后我们还需要一个按钮,当我们设置好后,点击这个按钮后就开始bake工作流程并把烘焙的顶点动画贴图导出。
现在还没完,我们的模型UV是用来给模型纹理映射用的,那我们的这张顶点动画贴图应该怎样将保存进贴图里的值取出来然后给对应的顶点呢。答案是分第二套UV,把顶点按照顺序排列起来,然后在sample的时候直接就把值取出来给到顶点了,这是在为在引擎里还原顶点动画作考虑了。
我们要完成我们的操作,首先需要有一个储存原始模型的变量,一个储存顶点数的变量来决定顶点数组的长度,当然还要一个储存模型顶点的数组。我们还需要一个二维数组用来储存每帧模型所有顶点的位置。我们还需要一个数组用来储存所有顶点对应的UV的位置。所以我们的代码变成了如下的样子:
macroScript TextureAnimation category: "Texture_Vertex_Animaton" buttontext: "Vertex Animation Tools" tooltip: "Vertex Animation Tools"
(
ResumeEditing()
escapeEnable = true
global targetMorphUV = 2
rollout TexMorphRollout "Vertex texture Animation Tool"
(
global originalMesh
global copyBaseMesh
global numberofVerts --原始模型的顶点树木
global originalMeshVertPositions = #()
global MorphTargetArray
global Morph_Floater
global internalArrayOfStaticBaseMeshes = #() --选中的模型们的一维数组
global vertexUVPosition = #() --储存顶点模型的UV的位置
global MorphNormalArray = #()
global MorphVertOffsetArray = #()
global MorphTargetProgressPercentage = 0.0
global masterMorphArray = #() --二维数组,第一层为选中的模型,第二层为那个模型对应时间范围内的所有snapshot
global noMeshesArray = #( " No meshes processed" as string )
group "Morpher Meshes"
(
spinner spinnerAnimationRangeStart "Anim Start" type: #integer range: [ 0 , 1000000 , animationRange.start ]
spinner spinnerAnimationRangeEnd "Anim End" type: #integer range: [ 0 , 1000000 , animationRange.end ]
spinner spinnerAnimationRate "Frame step Skip" type: #integer range: [ 0 , 1000000 , 0 ]
dropdownlist ddlTextureCoordinate "Texture Coordinate:" items: #( "2" , "3" , "4" , "5" , "6" , "7" , "8" ) tooltip: "用第二套UV来放顶点动画的顶点位置"
button CreateVertexAnimation "Create Vertex Animation"
)
button help "help"
/*******************************************************************************功能函数**************************************************************************************/
function CheckUnits =
(
if ( units.SystemType != #Centimeters )
then
(
messageBox "请校准好Max的系统单位,保持与Unity中的一致"
return false
)
else
(
return true
)
)
function CheckMesh selectmesh =
(
isvalidnode selectmesh and superclassof selectmesh == GeometryClass
)
function ClearMeshes =
(
if isValidNode masterMorphArray[ 1 ] and masterMorphArray.count > 0 do
(
delete masterMorphArray
masterMorphArray = #()
)
)
function updateProgAmount i myArrayCount =
(
MorphTargetProgressPercentage = ((i as float / myArrayCount as float ) * 100.0 )
progressUpdate MorphTargetProgressPercentage
if MorphTargetProgressPercentage == 100.0 do progressEnd()
if getProgressCancel() == true do
(
progressEnd()
) -- returns true if cancelled
)
function ReInitVarriables =
(
masterMorphArray = #()
MorphVertOffsetArray = #()
originalMesh = undefined
numberofVerts = 0
internalArrayOfStaticBaseMeshes = #()
MorphTargetProgressPercentage = 0.0
originalMeshVertPositions = #()
MorphNormalArray = #()
tempMorphArray = #()
)
function MakeSnapShotsReturnArray MeshToSnapShot =
(
progressStart "Create morph targets"
FrameArray = #()
NumOfFrames = floor ( spinnerAnimationRangeEnd.value - spinnerAnimationRangeStart.value )
for i = 0 to NumOfFrames by ( spinnerAnimationRate.value + 1 ) do
(
newtime = spinnerAnimationRangeStart.value + i
newCopy = at time newtime snapshot MeshToSnapShot
--deleteKeys newCopy #allKeys
meshop.unifyNormals newCopy #{ 1. . newCopy . numfaces }
append FrameArray newCopy
updateProgAmount i NumOfFrames
)
progressEnd()
return FrameArray
)
function attachMeshes mesh1 mesh2 =
(
if classof mesh1 == editable_poly then mesh1.attach mesh2 mesh1
else attach mesh1 mesh2
)
function fixUVNames polyToFix =
(
for i = 1 to ( polyop.getNumMaps polyToFix) do ( ChannelInfo.NameChannel polyToFix 3 i ( "UVChannel_" + i as string ))
)
function MakeAndMergeSnapShots ArrayOfMeshes =
(
if ArrayOfMeshes.count > 0 do
(
for i in ArrayOfMeshes do
(
--把每一帧的模型全部snapshot出来,并且保存在全局变量masterMorphArray二维数组中的第二维。
if CheckMesh i do append masterMorphArray (MakeSnapShotsReturnArray i)
)
--如果有多个有关键帧的原始模型,则会把每帧的两个模型的关键帧克隆attach到一起,如果没有,下面的逻辑没跑
masterMorphArray1Count = masterMorphArray[ 1 ]. count
if masterMorphArray.count > 1 do
(
for i = 2 to masterMorphArray.count do
(
for framecount = 1 to masterMorphArray1Count do
(
currentMasterObject = masterMorphArray[ 1 ][framecount]
attachMeshes currentMasterObject masterMorphArray[i][framecount]
)
)
)
masterMorphArray = masterMorphArray[ 1 ]
)
)
function SmoothCopyMesh Meshes =
(
OrgName = Meshes.name
originalMesh = at time 0 snapshot Meshes
originalMesh.name = OrgName + "_MorphUV" + (targetMorphUV as string ) + "_MorphExport"
s = smooth ()
s.smoothingBits = 1
addModifier originalMesh s
numberofVerts = getNumVerts originalMesh
originalMeshVertPositions = #() --清空位置数组,它是定义在全局的
if ClassOf originalMesh.baseobject == Editable_Poly then
(
for i = 1 numberofVerts do
(
append originalMeshVertPositions ( in coordsys world polyop.getVert originalMesh i)
)
)
else
(
for i = 1 to numberofVerts do
(
append originalMeshVertPositions ( in coordsys world getVert originalMesh i)
)
)
)
function PackVertexUVs myMesh =
(
progressStart "Packing the game mesh UVs"
convertTo myMesh Editable_Poly
for i = 1 to numberofVerts do
(
offset = 1.0 / (numberofVerts * 2 )
currentPosition = (((i as float ) - 0.5 ) / numberofVerts)
polyop.setVertColor myMesh targetMorphUV i [currentPosition * 255.0 , 128.0 , 0 ]
append vertexUVPosition currentPosition
updateProgAmount i numberofVerts
)
fixUVNames myMesh
progressEnd()
)
function getVertPos model index =
(
pos = [ 0 , 0 , 0 ]
if classof model.baseobject == editable_poly then (
pos =in coordsys world polyop.getVert model index
) else (
pos =in coordsys world getVert model index
)
return pos
)
function populateMorphTargetArrays =
(
progressStart "Creating the Morph Targets"
masterCount = masterMorphArray.count
for i = 1 to masterCount do
(
CurrentMorphTargetNormalArray = #()
currentMorphTarget = masterMorphArray[i]
global currentMorphVertexOffsetArray = #()
MorphTargetProgressPercentage = updateProgAmount i masterCount
for j = 1 to numberofVerts do
(
originalVertPos = originalMeshVertPositions[j]
currentModelVertPos = getVertPos currentMorphTarget j
currentOffset = (currentModelVertPos - originalVertPos)
currentOffset = [currentOffset[ 1 ], - 1.0 * currentOffset[ 2 ],currentOffset[ 3 ]]
currentOffset *= 255.0
append currentMorphVertexOffsetArray currentOffset
)
append MorphVertOffsetArray currentMorphVertexOffsetArray
append MorphNormalArray CurrentMorphTargetNormalArray
)
)
function Rendertexture =
(
fopenexr.SetCompression 0
fopenexr.setLayerOutputType 0 1 -- set layer 0 main layer to RGBA, RGB = 1
fopenexr.setLayerOutputFormat 0 1 --0 32 sets main layer to float 16 via 1. other options are 0 float 32, 2 int 32
global TextureName = getSaveFileName types: "EXR (*.EXR)|*.EXR"
if TextureName == undefined then
(
messagebox "please select a file location"
)
else
(
uvString = "_UV" + ((targetMorphUV - 1 ) as string )
TextureNameOffset = replace TextureName ( findString TextureName ".EXR" ) 4 (uvString + ".EXR" )
global FinalTexture = bitmap numberofVerts ( MorphVertOffsetArray.count ) filename: TextureNameOffset hdr: true ;
for i = 0 to ( MorphVertOffsetArray.count - 1 ) do
(
setPixels FinalTexture [ 0 , i] MorphVertOffsetArray[(i + 1 )]
)
save FinalTexture gamma:1.0
close FinalTexture
)
)
/*******************************************************************************UI交互函数**************************************************************************************/
on CreateVertexAnimation pressed do
(
/*判断一下系统单位是否和引擎保持一致*/
if (CheckUnits() == true ) do
(
try
with redraw off
(
ReInitVarriables()
/*把选中的模型压入数组*/
for i in selection do if CheckMesh i do append internalArrayOfStaticBaseMeshes i
geoConversionModelFailNamelist = #()
--遍历所有选中的需要处理的模型,把有问题的模型找出来
for i in internalArrayOfStaticBaseMeshes do
(
CopyMesh = convertTo ( snapshot i) Editable_Poly
if (( getNumVerts i) != ( getNumVerts CopyMesh)) then
(
append geoConversionModelFailNamelist i.name
)
delete CopyMesh
)
--如果找到了模型,则不会进行顶点动画的烘焙
if geoConversionModelFailNamelist.count > 0 then
(
string S = "模型有问题"
for i in geoConversionModelFailNamelist do append S ( " \r " + i)
messageBox S
)
else
(
if internalArrayOfStaticBaseMeshes.count > 0 then
(
--把选中的模型在指定范时间围的状态全部snapshot出来,并且把这些数据保存在二维数组masterMorphArray中
MakeAndMergeSnapShots internalArrayOfStaticBaseMeshes
SmoothCopyMesh masterMorphArray[ 1 ]
PackVertexUVs originalMesh
populateMorphTargetArrays()
ClearMeshes()
RenderTexture()
)
)
)
catch
(
messageBox "Catched Error !!!"
ResumeEditing()
)
)
ResumeEditing()
)
on help pressed do
(
S = #()
HelpString = ""
append S "第一步:输入顶点动画开始的位置。"
append S "第二步:输入顶点动画结束的位置。"
append S "第三步:输入顶点动画需要跳过的位置。"
append S "第四步:选择一个供顶点动画贴图sample的UV空间,默认使用第二套UV"
append S "第五步:点击生成顶点动画按钮,选择导出路径。"
for i in S do HelpString += i + " \r\r "
messageBox HelpString
)
/******************************************************************************************************************************************************************************/
)
if Morph_Floater != undefined then CloseRolloutFloater Morph_Floater
global Morph_Floater = newRolloutFloater "" 200 230
addRollout TexMorphRollout Morph_Floater
)
macros.run "Texture_Vertex_Animaton" "TextureAnimation"