【MAXScript】3DMax批量修改贴图名及模型名
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
【MAXScript】3DMax批量修改贴图名及模型名
一堆废话
想不到自己还会接触3DMAX的脚本语言——MAXScript。
首先申明一下,本人不是建模组的,对建模什么的不甚了解。
倒是想学来着,会点建模如果自己要独立开发一些小项目,也可以不麻烦人,如果可以,什么事情还是自己亲力亲为来得快。
可是同事说怕我抢了他的饭碗,愣是没有教我。
嘿嘿!其实我知道那不过是他的玩笑话,主要还是我自己没有时间去学啦,毕竟不是一朝一夕就能够学会的。
“这么多模型,这么多贴图要怎么改?”又听见建模组同事在仰天长哮~~~公司早期有很多项目是使用一款叫virtools(我也有幸用过一段时间,oh no!跟unity真的不是一个等级的,最关键的是网上完全没有资料可寻,碰到一个问题,可以折腾你一个星期也束手无策~)的引擎来开发的,后来客户要求将一些virtools项目改成unity3D,而virtools引擎支持中文命名的模型及贴图,但是众所周知,unity对中文的支持不是很好,经常因为中文发生莫名的错误,或者乱码之类的问题,所以不得不将这些模型和贴图的名称全部改成英文的。
“你们3DMAX有没有什么脚本之类的,可以使用脚本来批量改啊,一般软件都会自带脚本,以解决软件自身功能局限的问题。
”我刚说完,就感受到了来自对面的‘杀气’,只见同事恶狠狠的盯着我。
好吧!我错了,忘了同事压根没有写过代码,说了也是白说(我不是有意要打击你们的哟)。
“好吧,我帮你看看3DMAX有没有脚本,研究一下要怎么写。
”同事听罢,嘴角上扬。
唉,这奸笑意味着我又要折腾一番了。
于是,便开始了MAXScript编程之旅。
批量修改模型名
没有任何MAXScript基础该怎么开始呢?总要先了解一下该脚本语言的语法规则和常用API吧,于是上网下载了本电子书《3ds MAXScript 脚本语言完全学习手册》(各位看官可自行搜索下载,很好找哦,一搜一大把)。
(由于自己机子上没有装3DMAX,所以也
不方便附上测试的截图,但是代码之前都是在同事机子上实验过的,成功帮助他们改了模型名以及贴图名,所以在特定条件下是没有问题的,关于这个“特定条件下”以及可能会出现的问题在后面也会提到。
)
步骤:
1. 建立两个txt文件,一个是用于存放原模型名的列表,一个用于存放新模型名的列表(之所以分成两个txt文件,而不是一个txt文件存储两个列表,
是因为对maxscript比较陌生,使用两个列表省了解析字符串的麻烦);
2. 逐行读取两个txt文件的内容;
3. 读取一行,就搜索场景中名称匹配的模型,然后替换成新名称。
具体实现:
1. 只能手动解决,这个没有什么快捷方式。
2. 可以通过书中第三章
3.2.19节的FileStream(文件数据流)的OpenFile方法对文本文件进行I/O操作。
切记: maxscript的函数并不是以一对“()”括号来标识的,传参数的方法也不像其他常用语言那样在“()”里填入参数,而是在函数名后“ ”加“空格”然后在后面带上函数参数,如要读取D盘下的maxscript.txt文件内容,应如此写:
source = OpenFile "d:\maxscript.txt" --后面不带封号,不一样的注释符号"--";
•1
而不是:
source = OpenFile("d:\maxscript.txt");
•1
3. 这个步骤主要是获取指定名称的对象,找了好久,终于找到一个“好东西”:$(好像是叫“选择符”),用处貌似还蛮大,但是我只会它的一种功能:搜索场景中指定名称的模型物体,并返回该物体对象,如:要搜索一个名称叫“box1”的物体,可以采用如下方式:
$box1
•1
各位注意看了,有没有发现非常坑爹的一点,就是‘$’这个选择符后的box1居然不用加双引号,也就是box1被直接识别成了字符串,那么问题来了:如果我要传一个字符串变量该怎么办?假如,我有一个变量str,并搜索str所代表的模型,如下:
str = "box1" --不需要申明变量类型,应该是自动识别的
$str --如果是通过这样,那便会搜索名称为"str"的模型,而不是搜索"box1"
•1
•2
尝试很多方法也无法向‘$’传入变量,至此,也只能抛弃这个神奇的符号了(没有深入研究,不了解它的用法,各位如果知道怎么传入变量,还请留言告知,谢谢)。
好吧,继续翻看一下学习手册吧,看看有没有其他的方法。
就是你了:selection(当前选择对象集合),可参看手册“3.5.3 ObjectSet(对象集)”,几番测试,可以通过for 循环遍历selection中所有的模型对象,但是此法效率过于低下,因为找一个物体就要遍历一遍selection,没找到更好的方法,将就用吧,下面附上源码:
source = OpenFile "C:\Users\Administrator\Desktop\批量改名\原名.txt" --获取"原名.txt文件"的io流
des = OpenFile "C:\Users\Administrator\Desktop\批量改名\新名.txt" --获取"新名.txt文件"的io流
for j = 1 to 3 do --列表几条记录就写几条,maxscript习惯从1开始计数
(
str = readLine source --逐行读取文本内容
strD = readLine des
for obj in selection do --遍历场景中所有模型,注:selection 需要选中场景中的模型哦
(
if ==str then --判断模型名称是否与原名一致,如果一致,就替换成新名
(
= strD
exit --退出循环
)
)
)
•1
•2
•3
•4
•5
•6
•7
•8
•9
•10
•11
•12
•13
•14
•15
•16
•17
代码是不是有点简单?千万千万不要说我絮絮叨叨的侃了一堆,只写出了这么几行代码,要知道,这几行代码可都是精华呀,好啦,
下面才是重头戏。
批量修改贴图名
说起修改贴图名,真的不得不吐槽一下3DMAX以正常的方式修改贴图名的办法:先修改硬盘上该贴图的名字,然后再关联该贴图的路径,真真是个鸡肋,且不说几千张贴图了,就是几百张也要耗费诸多人力物力啊。
3DMAX没办法像Unity3D一样,在Project视图下修改一个文件名,自动对硬盘上相同的文件进行修改,而不需要再次关联。
所以,修改贴图名的小程序就在这样的背景下,应运而生了。
(说的有点夸张了哈,好像顺应了历史的潮流,历史发展的必然趋势,哈哈)
不废话,先从兜里掏出那万能的maxscript手册,直接翻开讲述贴图的那一章内容“11.3 TextureMap:Material(贴图)”,看看有没有什么惊喜。
只看到了这个东西和修改贴图名有关:assignNewName <TextureMap>,描述说明:“修改指定纹理贴图的名称,以保持其名称的惟一性。
修改后的名称格式为“Map #1”,其中数字表示该纹理贴图在场景中的创建序号。
”想来这种方法也是不行的,即使修改了场景中的贴图名,也无法修改硬盘上的贴图名,最后被修改的贴图也只能是missing了。
(这个方法我也没有试过,大家可以自行尝试。
)接着往下找的时候,看到了这样几句示例代码:rm = renderMap $foo.Material.diffuseMap size:[640,480] fileName: "foodif.bmp"
save rm
close rm
•1
•2
•3
•4
通过上述代码的启发,是不是可以通过(模型对象).Material.diffuseMap.fileName = “贴图路径”,其实在3DMAX 中,贴图名可看成是贴图在硬盘上的路径。
测试一下,首先在场景中
随便建个Box用于测试,先给其关联上一张贴图,然后再随便选择一个路径放一张其他的贴图,然后将其路径复制下来作为要修改的贴图名,然后打开maxscript侦听器,执行脚本,成功修改贴图名,并关联上硬盘的贴图文件。
应该说,核心的问题已经解决了。
写代码之前,照例整理一下思路,归纳一下步骤:
1. 保留原贴图路径及位置,将硬盘上的原贴图复制到另一个文件夹下,并修改贴图名,作为新贴图;
2. 将原贴图完整路径与新贴图完整路径分别写进两个txt列表中;
3. 读取两个txt文件,通过遍历模型,找到材质,找到贴图,再将贴图与txt的原贴图进行匹配,匹配成功后,替换成新贴图。
具体实现:
1. 如果对新贴图名的命名并没有什么要求,其实这一个步骤有一个快捷的方法,随便使用什么编程语言,写一个小程序,让程序帮你做复制贴图,并将贴图路径写入各自的txt文件中,几秒就可以搞定第一个步骤;如果对贴图名称有要求,那就比较麻烦了,要一个个改贴图名称,还要一一对应写入列表;
2. 参见1;
3. 使用openfile读取列表,selection遍历模型,并遍历每个模型的材质和贴图,然后替换贴图。
这个我在测试场景中(就只有几个模型)是没有问题的,后来在一个几千个模型的场景中就报错了,主要如下几个错误:a.关于“Multi-Material”的错误,错误原因:场景中存在多维材质,至此,我也知道了程序的局限性,之前的程序没有考虑到材质的种类,实际上只对标准材质有效果,这里也解释了我上文中提到的,程序只在特定条件下没问题,主要是因为没办法考虑所有的材质种类。
因为多维材质还比较常用,为了使程序局限性小一点,所以将多维材质添加进判断条件中,结果最最坑爹的事情你知道是什么吗?我判断了多维材质以后,将多维材质中的子材质按标准材质处理,结果又报错,原来多维材质中还有多维材质,结果在多维材质中又作了一次判断,所以看起来代码很长,复用性比较差,各位可以写一个方法封装,这里就不深究在maxscript中方法的定义了;b.关于
diffuseMap “undefined”的错误,错误原因:有些模型无材质,所以出现材质上的贴图未定义的异常;c.关于filename “undefined”的错误,错误原因:有些材质无贴图,所以出现贴图名称未定义的异常。
上代码:
遍历模型法
source = OpenFile "F:\新名.txt"
des = OpenFile "F:\原名.txt"
for j = 1 to 39 do
(
str = readLine source
strD = readLine des
print(str)
print(strD)
for i in selection do --遍历场景中所有选中的物体
(
s = i.Material as string --将材质属性转成字符串格式
if s!="undefined" then( --模型没有材质时,材质为"undefined"
if s.count>18 then
(
ss = substring s 1 18 --获得材质属性子字符串,用于判断是否是“多维材质”
)
--多维材质
if ss == "#Multi/Sub-Object:" then --子字符串中若含有多维材质的信息,则为多维材质,按多维材质处理
(
for k=1 to i.Material.MaterialList.count do --将多维材质中的子材质放入数组中,并遍历每个子材质中的贴图
(
multi = i.Material.MaterialList[k] as string --将多维材质中的子材质属性转成字符串格式
if multi !="undefined" then --若多维材质无子材质,则为"undefined"
(
dif = i.Material.MaterialList[k].diffuseMap as string --将贴图属性转成字符串格式
if dif !="undefined" then --若材质无贴图,则贴图为"undefined"
(
if i.Material.MaterialList[k].diffuseMap.filename==str then ( i.Material.MaterialList[k].diffuseMap.filename = strD
exit --退出循环
)
)
)
)
)
else --标准材质
(
difS = i.material.diffuseMap as string
if difS !="undefined" then
(
if i.material.diffuseMap.filename==str then
(
i.material.diffuseMap.filename = strD exit
)
)
)
)
)
)
•1
•2
•3
•4
•5
•6
•7
•8
•9
•10
•11
•12
•13
•14
•15
•16
•17
•18
•19
•20
•21
•22 •23 •24 •25 •26 •27 •28 •29 •30 •31 •32 •33 •34 •35 •36 •37 •38 •39 •40 •41 •42 •43 •44 •45 •46 •47 •48 •49 •50 •51
•52
•53
•54
•55
•56
和修改模型名一样的问题,此方法效率低下,耗时太长,当时一个场景5000多个模型,替换一张贴图要50s左右,因为替换一张贴图就要遍历5000多个模型,所以后来也一直在想有没有不通过遍历模型的方式。
比如遍历材质,相对而言,材质要少得多,但是也是很久没有发现比较好的方式。
那天下班回家又想起了这个问题,终于功夫不负有心人啊,无意中发现手册中3.5.13节一个叫MaterialLibrary(材质库)的类,类中有一个系统变量:SceneMaterials(场景里的材质的集合),于是又优化了一下程序,这次可快得多了,替换一张贴图只需要0.x秒,这么激动人心的时刻,快上代码:
遍历材质法
source = OpenFile "F:\贴图改名程序\原名.txt"
des = OpenFile "F:\贴图改名程序\新名.txt"
--b = false
for j = 1 to 176 do
(
str = readLine source
strD = readLine des
print(str)
print(strD)
start =timeStamp()
for i = 0 to SceneMaterials.count do --SceneMaterials:为系统变量,是一个包含场景中的所有材质的数组。
(
s = SceneMaterials[i] as string --将材质属性转成字符串格式if s!="undefined" then( --模型没有材质时,材质为"undefined"(此判断在通过遍历模型中的材质时用到)
if s.count>18 then
(
ss = substring s 1 18 --获得材质属性子字符串,用于判断是否是“多维材质”
)
--多维材质
if ss == "#Multi/Sub-Object:" then --子字符串中若含有多维材质的信息,则为多维材质,按多维材质处理
(
for k=1 to SceneMaterials[i].MaterialList.count do --将多维材质中的子材质放入数组中,并遍历每个子材质中的贴图
(
mat = SceneMaterials[i].MaterialList[k] --将材质赋值给 mat 变量。
multi = mat as string
if multi !="undefined" then
(
if multi.count>18 then
(
sss = substring multi 1 18
)
--多维材质子材质为多维材质
if sss == "#Multi/Sub-Object:" then
(
for subk=1 to mat.MaterialList.count do --将多维材质中的子材质放入数组中,并遍历每个子材质中的贴图
(
subMat = mat.MaterialList[subk] --将材质赋值给 subMat 变量。
submulti = subMat as string
if submulti !="undefined" then
(
if submulti.count>18 then
(
ssss = substring submulti 1 18
)
if ssss == "#Multi/Sub-Object:" then
(
)
else
(
subdif = subMat.diffuseMap as string
if subdif !="undefined" then
(
if subMat.diffuseMap.filename==str then
(
subMat.diffuseMap.filename = strD
--exit
)
)
)
)
)
)
--多维材质子材质为标准材质
else
(
dif = mat.diffuseMap as string
if dif !="undefined" then --若材质无贴图,则贴图为"undefined"
(
if mat.diffuseMap.filename==str then
(
--b = true
mat.diffuseMap.filename = strD
--exit
)
)
)
)
)
)
--标准材质
else
(
difS = SceneMaterials[i].diffuseMap as string
if difS !="undefined" then
(
if SceneMaterials[i].diffuseMap.filename==str then
(
SceneMaterials[i].diffuseMap.filename = replace SceneMaterials[i].diffuseMap.filename 1 SceneMaterials[i].diffuseMap.filename.count strD
--exit
)
)
)
)
)
end =timeStamp()
intervalTime = (end - start)/ 1000.0 print(intervalTime)。