3天学会MaxScript教程之(第二三天:编写一个高级Max顶点动画back到Texture的插件)

3天学会MaxScrip的第一天在这里: 点击打开链接

有了第一天的知识和初步认识,我们就来制作一个高级点的脚本插件吧。max脚本其实非常简单,主要知道语法就可以了,逻辑难度几乎为零。首先来看这个插件的效果

做动画其实有很多种方式,除了骨骼,目标变形,物理解算外,还有BackToTextureAnimation。其实这个和GPUSkin原理类似。
这个插件的原理是将所有顶点的位置数据拍成一列,然后再把每帧的这个数组再排到帧数组中,这样就能再Shader中读取顶点的位置,然后让顶点动画在引擎里还原了。

最后存出来是一张这种样子的图,它记录了模型位置的变化。

现在知道了原理,那么它是如何在max中生成然后导入引擎里,然后引擎的shader去读取识别然后还原的呢。下面就来一步一步制作。

首先建个文本文件,命名为VertexAnimationTool.ms

然后用VSCode打开。我们先创建一个max的工作窗口。

macroScript TextureAnimation category:"Texture_Vertex_Animaton" buttontext:"Vertex Animation Tools" tooltip:"Vertex Animation Tools"
(
    rollout TexMorphRollout "Vertex texture Animation Tool" 
    (
    )
    global Morph_Floater = newRolloutFloater "" 200 230 
	addRollout TexMorphRollout Morph_Floater
)
macros.run "Texture_Vertex_Animaton" "TextureAnimation" 

把脚本拖进去就能看到如下的效果了。

如果一切正常,下面我们来继续编写我们的工具。我们的工具需要指明我们需要Bake的动画帧范围,比如0到30帧的动画范围我们需要把它bake到我们的贴图里。所以我们需要一个指认动画范围的UI。同时我们的工具还需要动画的采样密度,0~30帧这个范围我们是把每帧都记录下来还是每隔一帧记录一次。然后我们还需要一个按钮,当我们设置好后,点击这个按钮后就开始bake工作流程并把烘焙的顶点动画贴图导出。

所以我们的代码变成了这样:

macroScript TextureAnimation category: "Texture_Vertex_Animaton" buttontext: "Vertex Animation Tools" tooltip: "Vertex Animation Tools"
(
    rollout TexMorphRollout "Vertex texture Animation Tool"
    (
        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 ]
            button CreateVertexAnimation "Create Vertex Animation"
        )
    )
    global Morph_Floater = newRolloutFloater "" 200 230
    addRollout TexMorphRollout Morph_Floater
)
macros.run "Texture_Vertex_Animaton" "TextureAnimation"

把脚本拖进max你将会看到:

现在还没完,我们的模型UV是用来给模型纹理映射用的,那我们的这张顶点动画贴图应该怎样将保存进贴图里的值取出来然后给对应的顶点呢。答案是分第二套UV,把顶点按照顺序排列起来,然后在sample的时候直接就把值取出来给到顶点了,这是在为在引擎里还原顶点动画作考虑了。

所以我们的工具还需要给模型指认一套UV,然后把模型的UV按照一定顺序排列成一排。

再给工具加上Help按钮,我们的工具UI声明代码如下
macroScript TextureAnimation category: "Texture_Vertex_Animaton" buttontext: "Vertex Animation Tools" tooltip: "Vertex Animation Tools"
(
    rollout TexMorphRollout "Vertex texture Animation Tool"
    (
        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"
    )
    global Morph_Floater = newRolloutFloater "" 200 230
    addRollout TexMorphRollout Morph_Floater
)
macros.run "Texture_Vertex_Animaton" "TextureAnimation"
我们要完成我们的操作,首先需要有一个储存原始模型的变量,一个储存顶点数的变量来决定顶点数组的长度,当然还要一个储存模型顶点的数组。我们还需要一个二维数组用来储存每帧模型所有顶点的位置。我们还需要一个数组用来储存所有顶点对应的UV的位置。所以我们的代码变成了如下的样子:
macroScript TextureAnimation category: "Texture_Vertex_Animaton" buttontext: "Vertex Animation Tools" tooltip: "Vertex Animation Tools"
(
    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"
    )
    global Morph_Floater = newRolloutFloater "" 200 230
    addRollout TexMorphRollout Morph_Floater
)
macros.run "Texture_Vertex_Animaton" "TextureAnimation"

我们的工具代码主要分为两部分,一部分为逻辑代码,一部分为UI交互代码。我们需要声明两个函数,来处理:

macroScript TextureAnimation category: "Texture_Vertex_Animaton" buttontext: "Vertex Animation Tools" tooltip: "Vertex Animation Tools"
(
    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"

        on CreateVertexAnimation pressed do
        (

        )
   
        on help pressed do
        (
              
        )

    )
    global Morph_Floater = newRolloutFloater "" 200 230
    addRollout TexMorphRollout Morph_Floater
)
macros.run "Texture_Vertex_Animaton" "TextureAnimation"

首先我们来补全Help函数:

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
        )

你将会看到如下效果:

下面我们来补全最为重要的CreateVertexAnimation函数。首先我们这个函数需要做以下几件事情

(1)先要判断模型资源,单位长度设置是否正确。不能有单独的点,没有用的点,或者说是破面啥的。

(2)把每一帧的模型SnapShot出来,然后把这一帧的顶点数组压入数组。
(3)创建一个原模型的克隆,然后给它分好UV和平滑组。
(4)清空每一帧创建的模型。
(5)把顶点数组烘焙到纹理上然后导出。

这个函数大概的结构是这样的,下面我们一步一步完善它

       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()
        )
首先我们有个try with catch结构,为了不让我们的程序出问题了直接就崩了,所以这里需要有个这个。

        function ReInitVarriables =
        (
            masterMorphArray = #()
            MorphVertOffsetArray = #()
            originalMesh = undefined
            numberofVerts = 0
            internalArrayOfStaticBaseMeshes = #()
            MorphTargetProgressPercentage = 0.0
            originalMeshVertPositions = #()
            MorphNormalArray = #()
            tempMorphArray = #()
        )
ReInitVarribles重新初始化我们的那个globle变量。
        function CheckMesh selectmesh =
        (
            isvalidnode selectmesh and superclassof selectmesh == GeometryClass
        )

CheckMesh是为了检查一次模型是不是集合体。
        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 ]
            )
        )

这里是给每一帧都创建一个snapshot。

然后把snapshot的顶点压入数组。
        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)
                )
            )
        )

了解这些核心函数后,我将我整个脚本的代码奉上:

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"

猜你喜欢

转载自blog.csdn.net/qq_16756235/article/details/79767235