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

相关文章

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机

Java中的for循环高级用法

《Java中的for循环高级用法》本文系统解析Java中传统、增强型for循环、StreamAPI及并行流的实现原理与性能差异,并通过大量代码示例展示实际开发中的最佳实践,感兴趣的朋友一起看看吧... 目录前言一、基础篇:传统for循环1.1 标准语法结构1.2 典型应用场景二、进阶篇:增强型for循环2.

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.

使用Python进行GRPC和Dubbo协议的高级测试

《使用Python进行GRPC和Dubbo协议的高级测试》GRPC(GoogleRemoteProcedureCall)是一种高性能、开源的远程过程调用(RPC)框架,Dubbo是一种高性能的分布式服... 目录01 GRPC测试安装gRPC编写.proto文件实现服务02 Dubbo测试1. 安装Dubb

SpringBoot连接Redis集群教程

《SpringBoot连接Redis集群教程》:本文主要介绍SpringBoot连接Redis集群教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 依赖2. 修改配置文件3. 创建RedisClusterConfig4. 测试总结1. 依赖 <de

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