前言

  最近的暑期项目为了制作广袤而庞大的花城汇广场,我们需要将店铺搭建出来,而这个过程既繁琐有无趣。
  为了加快进度,Mel插件俨然刻不容缓了。

  在搭建插件之前,我和建模团队商量了需要的店铺效果。
  其实并没有那么复杂,做好一个店铺的贴图面片,只要将其贴到相应的墙面上就大功告成了。
  并且很重要的一点是,面片需要根据墙面自动拉伸。

最初的思路

  MEL插件的思路说白了就是通过代码将一大堆的人工操作转换为机器执行。
  因此,我们先要将思路理清楚,才能将事半功倍。


  最初的思路

  • 导入面片模型
  • 缩放模型
  • 移动到相应位置进行匹配

  当我在Maya进行操作的时候,发现了不少问题。
  匹配位置需要匹配旋转轴,如何获取旋转角度却是非常棘手。
  如果使用缩放模型的话,难以判断缩放的比例。

snap together tool 获取转角度

  如何获取当前墙面的角度呢?
  确实这个不容易弄,毕竟墙面模型本身没有旋转角度,而有的墙就是斜的。
  更不用说如果旋转之后又冻结变换的情况了。

  那么需要考虑的是,如何吸附到墙面并且获取旋转角度问题了。
  而确实Maya也有相关的吸附命令。

吸附命令
吸附命令

  然而事与愿违,首先选择面去吸附对于自动化代码是极大地不便。
  而且,snap together的代码回显也非常扯淡。

吸附命令

  我清理了代码历史,并且开启了echo all command命令来显示所有的回显命令。
  然而执行之后的回显真让人头大。

1
2
3
4
5
6
7
8
{
float $rp[] = `xform -q -rotatePivot "|pCube1"`;
xform -worldSpace -preserve true
-rotatePivot 0 1.89666 6.70638 "|pCube1";
rotate -relative 0 207 0 "|pCube1";
xform -rotatePivot $rp[0] $rp[1] $rp[2] "|pCube1";
};
move -relative 0.448401 -1.89666 -5.82634 "|pCube1";

  上面的代码是众多Mel回显中,至关重要的部分,然而看看就知道有多扯淡。
  单看命令就知道它是如何实现旋转和位移的,但是角度和位移值确实莫名其妙出现的。
  而且这个命令不知道如何通过代码来选择面(mel没有回显相关的代码)
  这一系列的情况彻底给这种方法宣布了死刑。

利用吸附获取旋转角度

  Mel编程的难度逐渐在这里体现了,它既不是考验算法也不是考验编写,而是考验对Maya的熟悉程度。
  获取角度的另一种方法就是获取轴心点的旋转角。

轴心操作

  在移动轴心点的状态点击模型的component(点线面的总称),就可以将模型的轴心方向与选择component发现相匹配。
  然而这里遇到了和上面一样的问题。
  代码回显的时候角度已经莫名其妙地出现了。

Set to Face 获取旋转角度

  至此,我就陷入了僵局了……
  直到我想起了ADV5插件开发的时候,似曾相识地遇到同样的问题,后面似乎解决了。
  于是我翻找自己的旧插件,找到了关键的代码。

1
2
3
//举个例子(不是旧插件的代码)
{ string $Selection1[]; $Selection1[0] = "pCube1.f[2]";
manipMoveAlignHandleToComponent($Selection1[0], {"pCube1"}, {""}, "none", 0);; };

  然而这段代码是哪里来的呢?
  花了一阵子的功夫终于想了起来,Maya的隐藏吸附功能有核心代码!!!

吸附操作

  执行之后就可以获取到上面代码块的内容了。
  如此就能自动生成选择角度,只需要后期获取数值即可。

创建三角形

  话有说回来,角度值虽然是获取了,但是如果是冻结变换过的模型,又如何获取回到原点的旋转值呢?
  况且这里的模型更为复杂,如何才能获取到旋转角度呢?
  我想到的解决方案是通过三个点画出三角形,通过计算三角形到原点的旋转角度差返回需要的旋转角。

  那么如何才能画出三角形呢?
  可以用Create polygon tool来实现

Create polygon tool

  这样只需要选择三个点就可以根据三个点的坐标制作三角形了。

1
2
polyCreateFacet -ch on -tx 1 -s 1 -p -0.5 3.317463 0.5 -p -0.5 2.317463 0.5 -p 0.5 2.317463 0.5 ;
// Result: polySurface1 polyCreateFace1 //

获取精确的旋转值

  然而创建的三角形算是冻结变换的模型了,
  如何将一个冻结过的模型如何旋转回归到原点呢?
  这就需要bake pivot命令了。

烘焙枢轴

  bake pivot命令可以将模型回归到原点的状态数值,即便是冻结变换也没有问题。
  需要注意的是,角度烘焙需要在自定义轴上才能烘焙。
  恰好通过上面的吸附到面命令获取到的旋转轴已经是自定义的,完全不用担心这个问题。

烘焙枢轴

  如此一来就可以通过烘焙枢轴实现从原点到当前的旋转状态,解决了任意角度的旋转问题。


  不过后面我并没有采用这种方式。
  因为导入模型的朝向是不可控的,而这种方法必须要特定朝向才能正确执行。
  弄得不好,很容易出错。

  经过网上的资料查阅。
  后面我决定采用通过面法线计算角度的方法。

1
2
polyInfo -fn pCube1.f[0];
// Result: FACE_NORMAL 0: 0.000000 0.000000 1.000000

  可以看到polyInfo 可以返回面法线的信息,但是却是以字符串的形式返回
  这就需要截取字符串相关的数据,并且将数据转换为浮点数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//获取法线角度信息
string $NormalFaceStr[] = `polyInfo -fn pCube1.f[0]`;
string $buffer[];

//分离出需要的法线数据
tokenize $NormalFaceStr[0] " " $buffer;
//根据空格 将法线信息分割出来

print $buffer;
// FACE_NORMAL
// 0:
// 0.000000
// 0.000000
// 1.000000

//转换数据类型 用浮点数类型存储buffer中的数据字符串
float $Normal[];
$Normal[0] = $buffer[2];
$Normal[1] = $buffer[3];
$Normal[2] = $buffer[4];

  这样就获得了面法线信息了,但是如何计算角度呢?

1
angleBetween -euler -v1 1.0 0.0 0.0 -v2 $Normal[0] $Normal[1] $Normal[2]

  angleBetween可以计算两个向量角度。

初步总结

  以上将上面的代码汇总起来,那么第一步就差不多完成了。
  然而也还是遇到了一些坑,MEL没有像JavaScript数组push的功能。
  可能用python就没有那么操蛋的问题了。
  不够没有足够的时间去研究python了,因此得用mel解决。
  幸好网上找到了解决问题的代码。
  如此一来就方便我进行数据汇总操作。

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
57

////////////////////////////////////////////////////////////
//////////// 根据选择的点生成面 //////////////
///////////////////////////////////////////////////////////

string $sel[] = `ls -sl `;

//模拟 push数组 的效果将数据放到数组最后
global proc floatPush(float $array[],float $added[]){

int $addedSize = size($added);
int $arraySize = size($array);

for($i = $arraySize; $i < $arraySize+$addedSize; $i++ )
{
$array[$i] = $added[$i-$arraySize];
}

}

float $vtx_position[] = {};//清空数组

//将收集的点的位置信息批量放进数组中
for($j = 0; $j < size($sel); $j++ )
{
floatPush($vtx_position,`xform -q -ws -t $sel[$j]`);
}

print $vtx_position;

string $temp_tri[];

//通过数据生成三角面
string $temp_tri[] = `polyCreateFacet -ch off -tx 1 -s 1
-p $vtx_position[0] $vtx_position[1] $vtx_position[2]
-p $vtx_position[3] $vtx_position[4] $vtx_position[5]
-p $vtx_position[6] $vtx_position[7] $vtx_position[8]`;

////////////////////////////////////////////////////////////
//////////// 根据生成的面匹配旋转角度 //////////////
///////////////////////////////////////////////////////////

//获取法线角度信息
string $NormalFaceStr[] = `polyInfo -fn $temp_tri[0]`;
string $buffer[];

//分离出需要的法线数据
tokenize $NormalFaceStr[0] " " $buffer;

float $Normal[];
$Normal[0] = $buffer[2];
$Normal[1] = $buffer[3];
$Normal[2] = $buffer[4];

//获取旋转角度
float $rotation[] = `angleBetween -euler -v1 1.0 0.0 0.0 -v2 $Normal[0] $Normal[1] $Normal[2]`;