热烈欢迎新会员,新手报到帖maxscript BUG和技巧收集场景助手3.0下载飞浪脚本零起点入门
返回列表 发帖

欢迎捐助本站:支付宝账号 cgplusplus@qq.com,谢谢支持!

[原创]飞浪脚本零起点入门系列(十二)多边形编辑 Editable Poly

声明:本教程为CG++原创,请尊重作者劳动,转载请注明,谢谢:)
上一节:飞浪脚本零起点入门系列(十一)样条线编辑Editable Spline

飞浪脚本零起点入门系列(十二)多边形编辑 Editable Poly
本节关键词:Editable_Poly Method, EditablePoly,bitarray

在此系列教程中,本人一直试图把自己学习maxscript的经验奉献给读者,当然我的方法肯定不是最好的,但是只要这些经验能帮助更多喜欢maxscript的人,本人的目的就达到了。前面的所有章节中,不是所有的地方100%正确,比如第八节,我建议大家在变量名前面加前缀,后来我在其他书上看到说这个命名方法早就过时了。本人并不是计算机科班出身,对于程序的理解描述有些地并不专业,甚至还会出纰漏,如果你发现有些地方有问题请不要惊讶,还请不吝指教。
maxscript的语法就像英语的语法,内置函数就像单词,而编写maxscript就是把内置函数按照语法写成句子,再按照一定的逻辑排版出来,出来的就是一个完整的代码。这些内置函数全部在maxscript reference里面,它就是一部牛津词典。查词典大家都会,不认识的可以用金山词霸,而造句需要有想法。在maxscript里面,这些想法并没有程序专业讲的数据结构那么高深,简单的组织一下就可以完成高效的工作。本节涉及到的主要函数在下面两个网页:
Editable_Poly Methods
http://www.cgplusplus.com/online-reference/maxscript-reference/source/editable_poly_methods.htm
Interface: EditablePoly
http://www.cgplusplus.com/online-reference/maxscript-reference/source/interface_editablepoly.htm
下面跟我一起来走近Editable_Poly吧!
创建一个茶壶保持选中,运行以下代码:
  1. polyOp.getNumVerts $
复制代码
咦?出错啦,得到如下结果:
-- Runtime error: EPoly operation on non-Editable Poly: Teapot
它说EPoly的操作运行在非Editable Poly的物体上导致出错,所以本节所讲的操作全部要在Editable Poly下运行啊。
那我们就把它转成EPoly:
  1. convertTo $ Editable_Poly
复制代码

再运行第一个代码polyOp.getNumVerts $
得到结果530,这个是此茶壶的点的总数量。看上面那句代码,polyOp是一个已经编写好的结构,里面包括了许许多多的EPoly操作函数,运行一下polyOp得到如下结果:
#Struct:polyop(
  getEDataChannelSupport:<fn>,
  getMapFace:<fn>,
  makeEdgesPlanar:<fn>,
  capHolesByFace:<fn>,
  makeVertsPlanar:<fn>,
  setSlicePlane:<fn>,
  getEdgeVerts:<fn>,
  getDeadVerts:<fn>,
  getNumVerts:<fn>,
  ......
调用这些函数只需要用点语法即可。函数的说明在本文开头介绍的网页里面。polyOp.getNumEdges $是获取边的总数,polyOp.getNumFaces $是获取面的总数。下面我用场景助手里面的随机选择点、线、面、元素来说说EPoly的基本操作。
选择茶壶的一些点,运行代码:
  1. polyOp.getVertSelection $
复制代码
得到类似于下面的结果:
#{61..65, 129..132, 193..196, 246..247, 517..518}
这些是什么东东?getVertSelection的作用是获取选中的点,返回的就是这些点的信息了,那这结果有什么意义呢?其实我们得到的结果是一个bitarray,它是另一种数据常量,前面没有细讲。这里顺带讲一下。bitarray字面意思是位数组,我们知道数组#(1,2,3)用的是中括号,而位数组用的是大括号:#{1,2,3};数组里面的元素可以是任何数据,而位数组里面只能是正数。bitarray里面的数字只是一个开关,
表示在此bitarray里面,此位置是true还是false。怎么理解呢?bitarray对子元素的访问跟array一样,如有以下代码:
  1. bitarr=#{1,3,5,6,7}
  2. for i in 1 to 7 do print bitarr[i]
复制代码
得到的结果是true,false,true,false...,上面的#{1,3,5,6,7}表示1,3,5,6,7这四个位置是开启的,所以为true,用在EPoly可以表示选中了第1,3,5,6,7个点。而其他的数字在上面的bitarray里面都是false在EPoly里面可以表示这些点没有选中。可以用两个小点表示两个及以上的连续数字,如上面的bitarr可以写成#{1,3,5..7}。现在再回头看看上面得到的点的信息:#{61..65, 129..132, 193..196, 246..247, 517..518},这个bitarray在61到65的位置是true,所以这些点是选中的,后面类推。下面是对bitarray一个直观的解释:
  1. bitarr=#{1,3,5..7} --假设有一排灯,分别以1,2,3,...编号,这个数组就表示只打开了1,3,5,6,7号灯
  2. bitarr.count --表示此bitarr中的最大的数字(打开的灯中最大的编号),在这里是7,注意这里跟array不同
  3. bitarr.numberset --表示此bitarr中所有为true的元素的数量(所有打开的灯的数量),这里才是array里面的count
  4. bitarr[1]=false --把1号位的灯关掉
  5. bitarr[12]=true --把12号位的灯打开
  6. bitarr.count --此时打开的灯中最大的编号变成了12
  7. bitarr as array --把bitarr转变成了array,自己观察一下结果
  8. #(1,2,3,55,57,98,99,100) as bitarray --当然array也可以转变成bitarray,但是array里面的元素必须为正数
复制代码
回到EPoly中来,怎么样选中自己设定的点呢?执行如下代码:
  1. polyOp.setVertSelection $ #{1..100} --选中EPoly的第1到100个点,切换到点层级就能看到效果
复制代码

(转二楼)
3

评分人数

分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

欢迎捐助本站:支付宝账号 cgplusplus@qq.com,谢谢支持!

现在我们知道了点的总数量用polyOp.getNumVerts,选择点用polyOp.setVertSelection,那什么,来个随机选择点呗,来啦:
  1. vertTotal=polyOp.getNumVerts $ --点总数
  2. percent=0.3 --选择30%
  3. selVert=#{} --要选择的点的ID,这里是空的
  4. while selVert.numberset < vertTotal*percent do
  5. ( --当点数小于总数的30%时,不断的随机出数字,将selVert中对应的点设置为true
  6. selVert[random 1 vertTotal]=true
  7. )
  8. polyOp.setVertSelection $ selVert --选择点
  9. subobjectlevel = 1 --设置当前选择物体的子层级为1,EPoly即为点层级
  10. update $ --刷新一下物体,马上看到效果
复制代码
边的总量用polyOp.getNumEdges,选择边用polyOp.setEdgeSelection,子层级为2,把上面的代码替换一下就可以随机选择边了,同理面是polyOp.getNumFaces,选择用polyOp.setFaceSelection ,子层级为4,替换代码即可选择面,这些函数都从reference中来,没有什么高深的。下面来看看EPoly中的element。打开macro recoder,在element层级选择壶嘴,可以看到如下代码:
$.EditablePoly.SetSelection #Face #{321..384}
这是选择了ID号从312到384的面,如果用polyOp表示就是:
  1. polyOp.setFaceSelection $ #{321..384}
复制代码
壶嘴是一个元素element,但是显示的为什么还是选择面呢?其实,element就是一些ID号连续的面的集合,element本身是没有ID的,我们有办法用脚本选择指定的element吗?答案当然是有啦。在本文的开头介绍的第二个网页里面,有一个超级用的的函数SelectElement(),它可以根据当然选择的面,选择与这些面相关的element。执行如下代码:
  1. polyOp.setFaceSelection $ #{1} --选择ID号为1的面
  2. $.EditablePoly.SelectElement() --选择与选择的面为同一element的面,是不是有点绕口?
复制代码
这样就选择了包括ID号为1的element。那其他的element呢?既然element自身没有ID,那么我们就用face的ID号来标记,每个element只需要取出一个face ID出来就行了。理解一下下面的函数:
  1. fn getIDsOfElement obj =
  2. ( --此函数用来获取每个element中的第一个face ID
  3. select obj
  4. faceNum=polyOp.getNumFaces $
  5. ids=#{1} --储存face id
  6. tempID=1
  7. while tempID <= faceNum do
  8. ( polyOp.setFaceSelection $ #{tempID}
  9.   $.EditablePoly.SelectElement()
  10.   selFace=polyOp.getFaceSelection $
  11.   ids[tempID]=true
  12.   tempID+=selFace.numberset
  13. )
  14. ids as array --把结果转换成数组,便于使用
  15. )
复制代码
有了上面这个函数,我们就可以对element像对待face一样的操作了,对茶壶执行此函数:
  1. getIDsOfElement $
复制代码
得到结果#(1, 257, 321, 385)
此结果里面的数字是face ID,每个ID代表着一个element,那么对element随机操作也变得简单了:
  1. eleID=getIDsOfElement $ --获取与element相关的face ID
  2. percent=0.5 --选择50%的element
  3. selID=#() --需要选择的face ID
  4. while selID.count < eleID.count*percent do
  5. (
  6. tempID=eleID[random 1 eleID.count] --随机抽取一个ID
  7. if finditem selID tempID == 0 then append selID tempID
  8. )
  9. polyOp.setFaceSelection $ (selID as bitarray) --选择这些面,记得要转换成bitarray
  10. $.EditablePoly.SelectElement()
复制代码
上面讲了点、线、面、元素,EPoly的第三个层级没说,就是Border,它表示的是只有一个面用过的边,孤独的存在着,我们也不能把它遗忘啊,下面的代码直接选择这些边:
  1. subobjectlevel=3
  2. myEdges=polyOp.getOpenEdges $
  3. polyOp.setEdgeSelection $ myEdges
复制代码
到这里,相信大家对EPoly在maxscript中的操作已经有了一个认识了吧,本节所用到的函数只是EPoly操作中的几个而已,开头提到的两个网页的内容也不是全部的EPoly操作,帮助里面Frequently Asked Questions也有关于EPoly的例子,还是那句,函数都在帮助文档里面,你需要做的就是找出自己需要的函数然后按一定逻辑编写在一起,CG++里面的高手在逐渐增多,希望大家自由交流,快乐编程:)

补充:下面代码是分离出所有的Epoly,在老外的代码基础上更改过来的,polyop.getElementsUsingFace函数以前没发现。原贴地址:http://forums.cgsociety.org/showthread.php?f=98&t=785636 原来代码在15楼。更改后的代码:
  1. fn separateByFaceElements obj:selection[1] = if iskindof obj Editable_Poly do
  2. (
  3. local elements = #()
  4. local faces = obj.faces as bitarray
  5. while (f = (faces as array)[1]) != undefined do
  6. (
  7. ff = polyop.getElementsUsingFace obj #{f}
  8. append elements ff
  9. faces -= ff
  10. )
  11. local nodes = #()
  12. for k=1 to elements.count do
  13. (
  14. maxops.CloneNodes obj newNodes:&nn
  15. newname=uniqueName obj.name
  16. polyOp.detachFaces nn[1] elements[k] delete:on asNode:on name:newname
  17. centerPivot (execute ("$'"+newname+"'"))
  18. -- update nn[1]
  19. delete nn[1]
  20. )
  21. delete selection[1]
  22. )
复制代码
选择物体后运行:separateByFaceElements()

TOP

欢迎捐助本站:支付宝账号 cgplusplus@qq.com,谢谢支持!

支持,顶......

TOP

抢座……好好学习!
3ds max & Mental ray & MAXScript !

TOP

为什么我把我随机收集的数组转换成位数组后数量不对?少了几个……怎么回事?
  1. the_ary =for a = 1 to 64 collect random 1 (polyop.getnumverts $)
  2. the_ary.count
  3. bit_ary2 =the_ary as bitarray
  4. bit_ary2.numberset
  5. polyop.setvertselection $ bit_ary2
复制代码
3ds max & Mental ray & MAXScript !

TOP

支持斑竹 支持脚本

TOP

为什么我把我随机收集的数组转换成位数组后数量不对?少了几个……怎么回事?
feng523 发表于 2010-11-19 17:10



    第一句就有问题:
  1. the_ary =for a = 1 to 64 collect random 1 (polyop.getnumverts $)
  2. the_ary.count
复制代码

第一句中random 1 (polyop.getnumverts $)的随机数有可能有重复,所以第二句不一定都是你要的结果.

TOP

惭愧啊,昨天刚走出大楼就想出来了……呵呵,还是版主分析能力强
还有一个问题想请教一下版主,就是法线问题,怎么判断法线是不是统一的?怎么统一?情况是在多边形里有零星的法线翻转的面,不是被镜像过后的对象
有什么好办法吗?
3ds max & Mental ray & MAXScript !

TOP

我没有办法~~

TOP

……我心都凉了,竟然没有办法,很伤心。
3ds max & Mental ray & MAXScript !

TOP

返回列表