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

本文主要是介绍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"

这篇关于3天学会MaxScript教程之(第二三天:编写一个高级Max顶点动画back到Texture的插件)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/315582

相关文章

Nexus安装和启动的实现教程

《Nexus安装和启动的实现教程》:本文主要介绍Nexus安装和启动的实现教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Nexus下载二、Nexus安装和启动三、关闭Nexus总结一、Nexus下载官方下载链接:DownloadWindows系统根

CnPlugin是PL/SQL Developer工具插件使用教程

《CnPlugin是PL/SQLDeveloper工具插件使用教程》:本文主要介绍CnPlugin是PL/SQLDeveloper工具插件使用教程,具有很好的参考价值,希望对大家有所帮助,如有错... 目录PL/SQL Developer工具插件使用安装拷贝文件配置总结PL/SQL Developer工具插

python编写朋克风格的天气查询程序

《python编写朋克风格的天气查询程序》这篇文章主要为大家详细介绍了一个基于Python的桌面应用程序,使用了tkinter库来创建图形用户界面并通过requests库调用Open-MeteoAPI... 目录工具介绍工具使用说明python脚本内容如何运行脚本工具介绍这个天气查询工具是一个基于 Pyt

Java中的登录技术保姆级详细教程

《Java中的登录技术保姆级详细教程》:本文主要介绍Java中登录技术保姆级详细教程的相关资料,在Java中我们可以使用各种技术和框架来实现这些功能,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录1.登录思路2.登录标记1.会话技术2.会话跟踪1.Cookie技术2.Session技术3.令牌技

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

Java Spring 中的监听器Listener详解与实战教程

《JavaSpring中的监听器Listener详解与实战教程》Spring提供了多种监听器机制,可以用于监听应用生命周期、会话生命周期和请求处理过程中的事件,:本文主要介绍JavaSprin... 目录一、监听器的作用1.1 应用生命周期管理1.2 会话管理1.3 请求处理监控二、创建监听器2.1 Ser

maven中的maven-antrun-plugin插件示例详解

《maven中的maven-antrun-plugin插件示例详解》maven-antrun-plugin是Maven生态中一个强大的工具,尤其适合需要复用Ant脚本或实现复杂构建逻辑的场景... 目录1. 核心功能2. 典型使用场景3. 配置示例4. 关键配置项5. 优缺点分析6. 最佳实践7. 常见问题

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性