Published on

MaxScript学习笔记

Authors
  • avatar
    Name
    东哥
    Twitter

前言

MaxScript学习笔记

搬运至遥远的2019年

默认启动

\AppData\Local\Autodesk\3dsMax\2018 - 64bit\CHS\scripts\startup

把ms文件放到启动文件夹下,启动max就启动脚本

快捷添加

  • 用下面代码包裹一个ms代码,存为类型mcr格式
  • 放到路径\AppData\Local\Autodesk\3dsMax\2018 - 64bit\CHS\usermacros,重新启动max
  • 或者直接在编辑器模式下运行宏命令会直接添加到路径
macroScript VextexCacheTool   --具体类别里的名称             
        category:"VJ"    --显示的类别
        toolTip:""                        
	(
    -- 把ms脚本粘贴进来
    )

image-20200421095311706

语法记录

创建

mybox=box()
$Box:Box001 @ [0.000000,0.000000,0.000000]
mybox.lengthsegs=20
20
mybox.heightsegs=10
10
addModifier mybox (twist angle:30)
OK
mybox.twist.angle=99
99
mybox.twist.offset=10
10
addModifier mybox (spherify percent:50)
OK
mybox.spherify.percent=100
100

窗口

  • 独立单一窗口
try destroyDialog ::TestRoll catch() --关闭已有窗口
rollout TestRoll "TestRoll" 
(
    button myBtn "MyButton" width:200 height:100
  
)
CreateDialog TestRoll width:250 height:300 

效果

image-20200423175159452

  • 多个rollout
  global Morph_Floater = newRolloutFloater "" 200 230 
        

rollout tt "Test Tool" 
(
	button bt2 "test" height:30 width:200 
	
)
addRollout tt Morph_Floater

rollout tt2 "Test Tool2" 
(
	button bt2 "test2" height:30 width:200 
	
)
addRollout tt2 Morph_Floater

效果

image-20200423175135064

button

  • 设置图片
button BakeBtn "开始烘培" height:80 width:250 images:#(GetDir #maxSysIcons+"\CAT_CATMode_a.bmp", undefined, 1,1,1,1,1 ) iconSize:[200,80]

$

  • 一般用法是指选中的物体,比如
$.name --选中物体的名字
#.height	--选中物体的高度
  • 特定物体
$box01.height=10 --把名称为box01的物体的高度设置为10

动画

mybox =box()
animate on
(
  at time 0 (mybox.pos = [-100, 0, 0]; mybox.scale = [1, 1, 0.25])
  at time 100 (mybox.pos = [100, 0, 0]; mybox.scale = [1, 1, 3])
)--设置动画
playanimation()--播放

if

if mybox.height == 10
then mybox.width = 20
else mybox.width = 10
    
mybox.width = if mybox.height == 10 then 20 else 10   --类似C++ 的 ?:
        

        

==

== --equal to

!= --not equal to

> --greater than

>= --greater than or equal to

< --less than

<= --less than or equal to

for

 for var = value to array.count by -1 where (condition) do (
    
)
  • 创建一堆box
mybox =box()
for i = 1 to 10 do (
    copy_box=copy mybox
    copy_box.pos=[i*30,0,0]
    copy_box.wirecolor=[i*25,i*50,50]
)

image-20200421163756826

数组

arr = for i =1 to 5 collect i
--#(1, 2, 3, 4, 5)
arr2=#(1,2,3,555,44)
--#(1, 2, 3, 555, 44)
a = #()--空数组
a = #(1,2,3)
joint a #(4,5,6)-- a数组加入4,5,6
a.count --数量
c=($box* as Array)+($Sphere* as Array) --把所有box和spehre名称的作为数组并且相加
append c ($Cone* as Array) --把所有Cone作为数组成员添加到c

while

x=10
10
while x>0 do print(x-=1)
9
8
7
6
5
4
3
2
1
0
0

函数

fn fun1 v:0=(
if v==0 then print "good"
else if v>0 then messagebox ("greater than 0")
else messagebox ("too low")
)
        
fun1 v:-9

结构体

Struct person (name,sex,age)
joe=person name:"helo" sex:#male age:20
print(joe.name)

max命令

max file ? --显示file下面的命令 
max file open -- 打开文件

脚本:批量重命名

macroScript RenameThem   --运行以后生成宏,可以放到快捷方式上
    category:"HowTo"
    ButtonText:"RenameThem"
    toolTip:"Rename"

(
    
    rollout rename_rollout "Enter New Base Name"
    (
        edittext base_name ""
        button rename_them "RENAME"
        On rename_them pressed do 
        (
            if base_name.text!="" do (
                for i in selection do i.name=uniquename base_name.text  --uniquename保证不重名
            ) 
        ) 
    )
    CreateDialog rename_rollout 250 50
)

脚本:批量选择偶数面

rollout TestRoll "TestRoll" 
(
    button myBtn "MyButton" width:200 height:100
  
    on myBtn Pressed do (
        if selection.count==1 and classOf selection[1].baseobject==Editable_Poly then
            (
                local face_selection=#{}   --选择的面,值为bool
                local base_obj = $.baseobject   --选择的物体转换为baseobject
                local num_faces=polyop.getNumFaces (base_obj) --得到所有面
                print(num_faces)  
                for f=1 to num_faces do 
                (
                    local isSelect=((mod f 2)==0)  --选择偶数面
                   face_selection[f]=isSelect 
                ) 
                polyop.setFaceSelection base_obj face_selection --设置选择的面
                max modify mode	--改成修改模式
                modPanel.setCurrentObject base_obj	--把物体添加到修改面板里(后续会细讲)
                subobjectlevel = 4  --[[一个3ds Max的系统全局变量,让您获取和设置子对象层级在修改面板,如果它是开放的。

该值是一个零或更大的Integer,直到当前打开的修饰符支持的子对象级别数为止,通常按照“子对象”下拉列表中显示的顺序。

一种 subObjectLevel 为0表示关闭子对象模式。

如果“修改”面板未打开或当前修改器中不允许子对象级别设置,则全局变量包含该值 未定义 
                        
                        测试发现>=4才可以实现代码效果
                        ]] --

            )
            
        
      else messageBox ("error")
           
    ) 
)
CreateDialog TestRoll width:250 height:300 

控制菜单是否可以点击


on isEnabled return
(
 selection.count == 1 and classOf selection[1].baseobject == Editable_Poly
)

菜单开启/关闭

macroScript AutoMat category: "HowTo"
(
 local AutoMat_Enabled
 on isChecked return AutoMat_Enabled   --关键这一行
 on Execute do
 (
  if AutoMat_Enabled == undefined then
   AutoMat_Enabled = true
  else
   AutoMat_Enabled = not AutoMat_Enabled
  if AutoMat_Enabled then
  (
   txt ="if superclassof meditmaterials[4] != texturemap do \n"
   txt +="selection.material = meditmaterials[10]"
   callbacks.addscript #selectionSetChanged txt id:#AssignMaterial persistent:false
    Print "set mat"
  )
  else
   callbacks.removescripts id:#AssignMaterial
  updateToolbarButtons()
 )--end Execute
)--end macroScript
脚本:物体移动到自己的表面
macroscript MoveToSurface category: "HowTo"
(
fn g_filter o = superclassof o == Geometryclass
fn find_intersection z_node node_to_z =
(
 local testRay = ray node_to_z.pos [0,0,-1]  --参见下级目录ray
 local nodeMaxZ = z_node.max.z	--得到物体的max坐标系的z
 testRay.pos.z = nodeMaxZ + 0.0001 * abs nodeMaxZ  --修改ray的起始点坐标为物体z加上一点点
 intersectRay z_node testRay
)
on isEnabled return selection.count > 0
on Execute do
(
 target_mesh = pickObject message:"Pick Target Surface:" filter:g_filter 
 if isValidNode target_mesh then
 (
  undo "MoveToSurface" on   --注册撤销操作,否则无法撤销
  (
   for i in selection do
   (
    int_point = find_intersection target_mesh i
    print(int_point)
    if int_point != undefined then i.pos = int_point.pos
   )--end i loop
  )--end undo
 )--end if
)--end execute
)--end script

射线变量 ray

local testRay = ray node_to_z.pos [0,0,-1]  --射线变量,第一个变量是起点,第二个变量是方向

ray文档

射线方法 intersectRay

文档

 intersectRay z_node testRay 

该内置的intersectRay函数被赋予一个节点和一条光线(具有起点和方向的空间矢量),并返回空间中光线照射到节点表面的点;如果没有相交,则返回未定义的点。相交的结果也将是函数的返回值,因为它是最后计算的值。

pickObject

  • 原型
pickObject [	message:<string> ] [	prompt:<string> ] \ 
[	count:n|#multiple ] [	filter:fn ] \ 
[	select:<boolean> ] [	pickFrozen:<boolean> ] \ 
[	rubberBand:<point3>] [	rubberBandColor:<color> ] \ 
[	forceListenerFocus:<boolean> ] 
  • 案例
target_mesh = pickObject message:"Pick Target Surface:" filter:g_filter 

第一个参数用于显示在信息窗口的提示

image-20200422141811636

filter参数传入一个条件函数,案例里用来判断是否是几何体

扩展脚本:移动所有物体的Z坐标与选定物体对齐

macroscript MoveAllGeometryToLand category: "HowTo"
(
fn g_filter o = superclassof o == Geometryclass
fn getZ node_land=
(
    local testray=ray [0,0,-1000] [0,0,1]
    print(intersectRay node_land testray) 
   
)

on isEnabled return selection.count == 1
on Execute do
(
    target_mesh = pickObject message:"Pick Target LandMesh:" filter:g_filter
    if isValidNode target_mesh then
        (
            undo "MoveAllGeometryToLand" on
            (
                for i in geometry where((g_filter i)and i!=target_mesh) do
                    (
                        local point=getZ selection[1]
                        if point!=undefined then i.pos.z=point.pos.z

                    )
                )
            )
       

)

)--end script

路径定义

路径说明文档

脚本:录制并播放动画

macroScript QuickPreview category: "HowTo"
(
 preview_name = (getDir #preview)+"/quickpreview.avi"  --保存的文件路径
 view_size = getViewSize()  --得到视口大小,2位向量
 anim_bmp = bitmap view_size.x view_size.y filename:preview_name  --创建位图
 for t = animationrange.start to animationrange.end do
 (
  sliderTime = t
  dib = gw.getViewportDib()  --得到dib
  copy dib anim_bmp --把dib信息拷贝到bmp位图里
  save anim_bmp --保存位图
 )
 close anim_bmp
 gc()
 ramplayer preview_name ""  --调用raw播放器预览
)

脚本:创建自定义模型

radius1 = 100
radius2 = 10
width = 10
 
vert_array = #()
face_array = #()
 
vert_count = 0
num_faces = 10
 
for a = 0 to (360-(360/num_faces)) by 360/num_faces do
(
v1 = [radius1*cos(a+width),radius1*sin(a+width),0]
v2 = [radius1*cos(a-width),radius1*sin(a-width),0]
v3 = [radius2*cos(a),radius2*sin(a),0]
append vert_array v1   --添加点到数组
append vert_array v2
append vert_array v3
 
append face_array [vert_count+1,vert_count+3,vert_count+2]
vert_count += 3
)
 
m = mesh vertices:vert_array faces:face_array

尝试

vert_array=#()
faces_array=#()
h=20
iv=0
num=10
l=100
for i=1 to num do
(
     
             v1=[l/num*(i-1),0,0]
			 v2=[l/num*i,-h,0]
             v3=[l/num*i,h,0]
            
            append vert_array v1
            append vert_array v2
			append vert_array v3
            append faces_array [iv+1,iv+2,iv+3]
            
            iv+=3
            print iv
        
    
)

m = mesh vertices:vert_array faces:faces_array

image-20200423140625851

bitmap

bitmap <width <height> [filename:<filename_string>] \ 
[numframes:<integer>] \
[color:<color>] \ 
[gamma:<float>] \ 
[pixelAspect:<float>] \ 
[channels:<channel_name array>]\ 
[hdr:<bool>] \
[iconName:<filename>]\
[iconSize:<point2>]
  • 示例
b=bitmap 100 100 color:white
for i=0 to 100 do
	(
		setPixels b [i,i] #(red)
	)

display b

b.filename=@"d:t.bmp"
save b

image-20200424113002291

image-20200424113018711

EXR格式输出

文档

Epic顶点动画插件代码分析

法线

得到法线

fn getTheVertexNormal processObject vertexIndex = ( 
    normal = [0.0,0.0,0.0] 
    if classof processObject.baseobject == Editable_Poly then (
            vertexPolygons = polyOp.getFacesUsingVert processObject vertexIndex
            for i in vertexPolygons do (
                    normal+=in coordsys world polyOp.getFaceNormal processObject i
                )
            ) else (
                normal= getNormal processObject vertexIndex
            )
            normal=normalize normal 
            normal 
        )
  • 如果是可编辑多边形,得到一个顶点序号所有法线之和
  • 否则就根据getNormal方法直接去要

法线颜色

oldnormal=((((normalize (getTheVertexNormal currentMorphTarget j))*[1.0,-1.0,1.0])+1.0)*0.5)*255.0   --法线顶点色数据
  • [-1,1]转换成[0,1]再乘以255得到颜色

顶点偏移

位置和颜色

--遍历模型所有顶点
for j=1 to numberofVerts do (
        oldnormal=((((normalize (getTheVertexNormal currentMorphTarget j))*[1.0,-1.0,1.0])+1.0)*0.5)*255.0
        append CurrentMorphTargetNormalArray oldnormal
        originalVertPos=originalMeshVertPositions[j]
        currentModelVertPos=getVertPos currentMorphTarget j  --得到顶点位置
        if (captureAbsolutePositions.checked) 
            then (
                currentOffset=currentModelVertPos
            )
        else (
                currentOffset=(currentModelVertPos-originalVertPos)--如果不是绝对位置就要减去本地坐标来得到世界坐标
            )
            --反转Y轴
            currentOffset=[currentOffset[1],-1.0*currentOffset[2],currentOffset[3]]
            currentOffset*=255.0            --转成颜色
            append currentMorphVertexOffsetArray currentOffset
        )
--从系统方法得到顶点位置
fn 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
        )
        pos
    )

输出贴图

fn renderOutTheTextures = (	
    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)
            TextureNameNormal= replace TextureName (findString TextureName ".EXR") 4 (uvString+"_Normals.BMP")
            TextureNameOffset= replace TextureName (findString TextureName ".EXR") 4 (uvString+".EXR")
            global FinalTexture = bitmap numberofVerts (MorphVertOffsetArray.count) filename:TextureNameOffset hdr:true; --创建EXR贴图
            global FinalMorphTexture = bitmap numberofVerts (MorphVertOffsetArray.count) filename:TextureNameNormal hdr:true  gamma:1.0 ;--创建法线贴图
            --遍历所有顶点数,设置像素颜色
            --2个颜色数组用UE4的语法表示是个数组  TArray<`TArray<FVector>`>
            --所以一个成员代表一行的所有颜色
            for i=0 to (MorphVertOffsetArray.count-1) do (
                    setPixels FinalTexture [0, i] MorphVertOffsetArray[(i+1)]
                    setPixels FinalMorphTexture [0, i] MorphNormalArray[(i+1)]
                )
                save FinalTexture gamma:1.0
                close FinalTexture

                save FinalMorphTexture gamma:1.0
                close FinalMorphTexture
            )
        )