當前位置:首頁>軟件教程>maya教程>教程內容

mel語初解之二-多邊型建模(4)

來源: 作者:七月冰兒 學習:4168人次

眾所周知,maya的多邊形建模能力是不如人意的,因此這時mel會派上很大的用場。相信很多人都用過一些輔助性的建模工具,例如MJPolyTools、BPT、icePolyTools...我在教程中會對這些程序的關鍵功能的編寫方法作出詳細說明,希望大家能在掌握這些功能的基礎之上編寫出自己稱心如意的Poly工具。

現(xiàn)在繼續(xù)mel的教程,如果你對mel還一無所知的話,請看mel語初解之一-基礎和界面篇:
http://www.theoat.com.cn/photoshop/jiaocheng/106791.html

眾所周知,maya的多邊形建模能力是不如人意的,因此這時mel會派上很大的用場。相信很多人都用過一些輔助性的建模工具,例如MJPolyTools、BPT、icePolyTools、CPS、drawSplit、rockGen...我在教程中會對這些程序的關鍵功能的編寫方法作出詳細說明,希望大家能在掌握這些功能的基礎之上編寫出自己稱心如意的Poly工具。

mel作為腳本語言使用非常方便,在工作中會很容易地把你的一些簡單想法付諸實踐。

在講Poly建模之前,需要復習一下以前的知識。

首先要復習一下數(shù)組(Array):一群變量放到了一起,這群變量就成了一個數(shù)組變量。不過這些變量不是隨便放的,每個變量都有一個房間,每個房間都有順次的門牌號,我們就是根據(jù)門牌號來訪問任何一個數(shù)組成員的。請看這個字符串數(shù)組的例子:

選擇幾個場景中的物體。

// 獲取場景中的每一個物體,分別放入數(shù)組$objects的每個房間中
string $objects[] = `ls -sl`;

這時數(shù)組的狀態(tài)如圖所示。$objects可以看作是公寓的名稱,[]里的紅色數(shù)字為房間的門牌號,也叫作索引號(index)。數(shù)組的索引號總是從0開始的。也就是說$objects[0]為數(shù)組的第一個成員,它的值為"pSphere1";而$objects[1]為數(shù)組的第二個成員,他的值為"pCube1";以此類推。

我們可以從數(shù)組中取值,例如:
string $obj = $objects[0];
// 此時變量$obj的值為"pSphere1"

也可以給數(shù)組的成員賦值,例如:
$objects[1] = "pBox1";
// 此時數(shù)組$objects的值為{"pSphere1", "pBox1", "pCone1"}

要想遍歷數(shù)組中的每個成員,可以用for語句,有兩種方法。

// 方法一
string $objects[] = `ls -sl`;
for ($i = 0; $i < size($objects); $i++)
{
string $obj = $objects[$i];
// do something ...
}

// 方法二
string $objects[] = `ls -sl`;
for ($obj in $objects)
{
// do something ...
}
[注] mel的for...in語句和JavaScript有所不同,$obj是字符串,指的是
當前的數(shù)組成員,等同于"string $obj = $objects[$i];"

再復習一下函數(shù)(Function):

如果你編寫比較復雜的程序,就會發(fā)現(xiàn)有很多經常用到的語句,這些語句經常以相同的組合出現(xiàn)。這樣的語句編寫起來有些麻煩,看起來也不太直觀。為了提高工作效率,增加可讀性,我們可以使用函數(shù)把它們封裝起來。下面舉例說明。

還記得前面講過的filterExpand獲取多邊形面的方法吧?

string $faces[] = `filterExpand -ex 1 -sm 34`;

對初學者來說,看到"-sm 34"后,總是很難聯(lián)想到多邊形的面。當然你可以用maya的全局變量$gSelectMeshFaces來替代34,不過這樣做有些麻煩。我們編一個新的函數(shù)來做與上面代碼同樣的事情。

proc string[] getSelFaces()
{
return `filterExpand -ex true -sm 34`;
}
// [注] Sel為Selected的縮寫

有了這個函數(shù),我們以后再獲取多邊形的面時,就可以這樣寫:string $faces[] = `getSelFaces`;

也可以這樣寫:string $faces[] = getSelFaces();

return為返回的意思,proc后面的字代表返回值的類型,return后面的字(變量或表達式)代表返回值,也就是函數(shù)的輸出值。

對初學者來說,看到"-sm 34"后,總是很難聯(lián)想到多邊形的面。當然你可以用maya的全局變量$gSelectMeshFaces來替代34,不過這樣做有些麻煩。我們編一個新的函數(shù)來做與上面代碼同樣的事情。

proc string[] getSelFaces()
{
return `filterExpand -ex true -sm 34`;
}
// [注] Sel為Selected的縮寫

有了這個函數(shù),我們以后再獲取多邊形的面時,就可以這樣寫:string $faces[] = `getSelFaces`;

也可以這樣寫:string $faces[] = getSelFaces();

return為返回的意思,proc后面的字代表返回值的類型,return后面的字(變量或表達式)代表返回值,也就是函數(shù)的輸出值。

再看一個例子:

proc string[] getPolySel(string $type)
{
if ($type == "vert")
return `filterExpand -ex true -sm 31`;

if ($type == "edge")
return `filterExpand -ex true -sm 32`;

if ($type == "face")
return `filterExpand -ex true -sm 34`;

// 假如輸入?yún)?shù)是非預期的,就返回一個空數(shù)組
string $null[];
return $null;
}
想要獲取多邊形的面時,可以這樣寫: string $faces[] = getSelFaces("face");

或: string $faces[] = `getPolySel "face"`;

這回用到了函數(shù)的輸入?yún)?shù)(string $type),根據(jù)輸入?yún)?shù)的不同,產成不同的返回值。

一個函數(shù)可以既沒有輸入?yún)?shù)也沒有返回值,也可以只有其一。參數(shù)可以是多個,返回值只能是一個。

return語句執(zhí)行之后,后面的語句將不再執(zhí)行。例如:
 

proc myProc()
{
// 獲取選擇的物體
string $objects[] = `ls -sl`;
// 如果什么都沒選擇,就返回(什么也不做)。
if (!size($objects))
return;

// do something ...
}
proc myProc()
{
// 獲取選擇的物體
string $objects[] = `ls -sl`;
// 如果什么都沒選擇,就返回(什么也不做)。
if (!size($objects))
return;

// do something ...
}

或: string $faces[] = `getPolySel "face"`;

這回用到了函數(shù)的輸入?yún)?shù)(string $type),根據(jù)輸入?yún)?shù)的不同,產成不同的返回值。

一個函數(shù)可以既沒有輸入?yún)?shù)也沒有返回值,也可以只有其一。參數(shù)可以是多個,返回值只能是一個。

return語句執(zhí)行之后,后面的語句將不再執(zhí)行。例如:
 

proc myProc()
{
// 獲取選擇的物體
string $objects[] = `ls -sl`;
// 如果什么都沒選擇,就返回(什么也不做)。
if (!size($objects))
return;

// do something ...
}
proc myProc()
{
// 獲取選擇的物體
string $objects[] = `ls -sl`;
// 如果什么都沒選擇,就返回(什么也不做)。
if (!size($objects))
return;

// do something ...
}

global procproc的區(qū)別

proc是局部函數(shù),局部函數(shù)只能在編寫這個函數(shù)的mel文件中使用,不能在其他mel文件中使用,不能作為菜單和按鈕命令,不占用內存空間。

global proc是全局函數(shù),沒有proc那些局限。使用全局函數(shù)應注意,函數(shù)名不能與Maya中已有的全局函數(shù)或mel命令相同,否則會把原來的覆蓋掉,可以通過使用函數(shù)名前綴來避免重復命名。關于全局函數(shù)的使用,最好了解一些Maya的運行方式。Maya啟動時一般只把指定scripts路徑中的*.mel文件名(*)載入內存,這樣Maya運行時就可以調用這個文件中的同名函數(shù),而當調用這個同名函數(shù)時,這個mel文件中的所有全局函數(shù)將被載入內存,直到Maya退出。

如果還不明白,那就統(tǒng)統(tǒng)使用global proc好了,沒什么大不了的。

下面提供幾個多邊形建模常用到的函數(shù),因為后面經常用到,所以應該熟練掌握,至少對于每個函數(shù)做什么事要很清楚。
// 獲取選擇的多邊形頂點
proc string[] getSelVerts()
{
return `filterExpand -ex true -sm 31`;
}

// 獲取選擇的多邊形邊
proc string[] getSelEdges()
{
return `filterExpand -ex true -sm 32`;
}

// 獲取選擇的多邊形面
proc string[] getSelFaces()
{
return `filterExpand -ex true -sm 34`;
}

// 獲取選擇的多邊形UV點
proc string[] getSelUVs()
{
return `filterExpand -ex true -sm 35`;
}

用法范例:

// 獲取選擇的所有面,存放到數(shù)組$faces[]中
string $faces[] = getSelFaces();
    這四個函數(shù)是maya內置的,也是菜單命令,經常用到。

// 菜單命令:Edit Polygons->Selection->Convert Selection to Vertices
// 轉換當前選擇為頂點

ConvertSelectionToVertices();

// 菜單命令:Edit Polygons->Selection->Convert Selection to Edges
// 轉換當前選擇為邊

ConvertSelectionToEdges();

// 菜單命令:Edit Polygons->Selection->Convert Selection to Faces
// 轉換當前選擇為面

ConvertSelectionToFaces();

// 菜單命令:Edit Polygons->Selection->Convert Selection to UVs
// 轉換當前選擇為UV點

ConvertSelectionToUVs();

用法范例:

// 獲取選擇的所有面,存放到數(shù)組$faces[]中
string $faces[] = getSelFaces();
    這四個函數(shù)是maya內置的,也是菜單命令,經常用到。

// 菜單命令:Edit Polygons->Selection->Convert Selection to Vertices
// 轉換當前選擇為頂點

ConvertSelectionToVertices();

// 菜單命令:Edit Polygons->Selection->Convert Selection to Edges
// 轉換當前選擇為邊

ConvertSelectionToEdges();

// 菜單命令:Edit Polygons->Selection->Convert Selection to Faces
// 轉換當前選擇為面

ConvertSelectionToFaces();

// 菜單命令:Edit Polygons->Selection->Convert Selection to UVs
// 轉換當前選擇為UV點

ConvertSelectionToUVs();

這四個函數(shù)在maya的scripts/others目錄中,可以直接調用。

// 轉換當前選擇為頂點,并獲取這些頂點的名稱
global proc string[] getVerts()
{
select -r `polyListComponentConversion -tv`;
string $result[]=`filterExpand -ex true -sm 31`;
return $result;
}

// 轉換當前選擇為邊,并獲取這些點的名稱
global proc string[] getEdges()
{
select -r `polyListComponentConversion -te`;
string $result[]=`filterExpand -ex true -sm 32`;
return $result;
}

// 轉換當前選擇為面,并獲取這些面的名稱
global proc string[] getFaces()
{
select -r `polyListComponentConversion -tf`;
string $result[]=`filterExpand -ex true -sm 34`;
return $result;
}

// 轉換當前選擇為UV點,并獲取這些UV點的名稱
global proc string[] getUVs()
{
string $uvs[];
$uvs=`polyListComponentConversion -tuv`;
if (size($uvs) == 0) return $uvs;
select -r $uvs;
string $result[]=`filterExpand -ex true -sm 35`;
return $result;
}
// 根據(jù)點、邊、面、UV點的名稱得出多邊形的名稱
// 例如多邊形一條邊的名稱為"pSphere1.e[637]",則這個多邊形的
// 名稱為"pSphere1"

proc string getBaseName(string $item)
{
string $buffer[];
if ($item != "")
{
tokenize($item, ".", $buffer);
}
return $buffer[0];
}

用法范例:

string $polyName = getBaseName("pSphere1.e[637]");

// 返回值:pSphere1
// 根據(jù)點、邊、面、UV點的名稱得出它們的索引號
// 例如多邊形一條邊的名稱為"pSphere1.e[637]",則這個多邊形的
// 索引號為637

proc int getIndex(string $indexString)
{
string $buffer[];
tokenize($indexString, "[]", $buffer);
int $index = (int)$buffer[1];
return $index;
}

用法范例:

int $index = getIndex("pSphere1.e[637]");
// 返回值:637

首先介紹一個有用的函數(shù)(intersectStringArray)。這個函數(shù)可以找到兩個數(shù)組的共同部分,比如數(shù)組1為{"兔子", "老虎", "山羊", "蟲子"},數(shù)組2為{"蟲子", "刀子", "梳子", "兔子", "珠子"},你可以獲得一個新數(shù)組包含它們的共同部分{"兔子", "蟲子"}。

// 獲得兩個數(shù)組的共同部分
proc string[] intersectStringArray(string $array1[], string $array2[])
{
global string $m_arrayIntersector;
if ($m_arrayIntersector == "")
$m_arrayIntersector = `stringArrayIntersector`;

stringArrayIntersector -edit -intersect $array1 $m_arrayIntersector;
stringArrayIntersector -edit -intersect $array2 $m_arrayIntersector;
string $result[] = `stringArrayIntersector -query $m_arrayIntersector`;
stringArrayIntersector -edit -reset $m_arrayIntersector;
return $result;
}
    [注] global string代表一個全局字符串變量,以前講過全局變量應當盡量避免命名沖突。maya中的全局變量都是以
小寫字母"g"開頭,為避免沖突,本教程中的全局變量一律使用"m_"作為前綴。
    前面介紹過的函數(shù)可以看作是工具函數(shù),這些函數(shù)幾乎在以后的每個程序中都要用到。如果編寫某一功能,還需要編寫
一些有針對性的專用函數(shù),F(xiàn)在我們來編一個多邊形的導角功能,來看看一個完整的程序是怎樣完成的。
這是一些必須記住的單詞,相信所有學過Maya的人都不會感到陌生。

單詞 縮寫 解釋
polygon poly 多邊形
vertex v;ver;vert;vtx 多邊形頂點
edge e;ed 多邊形邊線
face f 多邊形面
split 切割
index idx 索引
    要編寫一個比較復雜的程序,我們首先考慮的是應該怎樣把這個程序做最大程度的簡化,要把一個龐大的東西拆成一小塊
一小塊的分別去處理。今天我們需要完成第一小塊,就是當你選擇一條邊時,程序可以在這條邊的兩側各切一刀,如圖。

要做到這一點,需要分成四步。

第一步,我們需要做一點準備工作,要了解一下切割命令polySplit和邊的構造順序。

為了更直觀的說明程序的原理,我盡量多放一些插圖。

選擇菜單Polygons->Create Polygon Tool,從左上角開始,畫一個正方形。這時看看mel歷史窗,可以看到polyCreateFacet命令,這個命令目前還用不到,先不去管他。

依次選擇正方形的四個頂點,看看每個頂點的名稱和索引號。

可以發(fā)現(xiàn)索引號是按照創(chuàng)建時的順序指定的。分別為0,1,2,3。

再看看每條邊的索引號,也是按照創(chuàng)建時的順序指定的。一條邊有兩個點,分別為起點和終點,這兩個點決定了邊的構造順序。

使用Edit Polygons->Split Polygon Tool在正方形上切一刀。

我們看一下polySplit的用法,-ep后面有兩個參數(shù),第一個參數(shù)(3)是邊的索引號,第二個參數(shù)(0.263489)是百分比,如果邊的長度為1,切割點在邊的0.263489處。

切割點位置的受到邊的構造順序的影響,以polySurface1.e[3]這條邊為例,從邊的起點開始,沿著邊的終點方向量出整條邊的約26%的長度,這個位置就是切割點的位置。

使用polySplit的一大難點就是判斷邊的構造順序,也就是分清邊的起點和終點。為了做到這一點,我們需要用到一個mel命令 - polyInfo。

選擇一條邊線(e[3]),在命令行執(zhí)行"polyInfo -ev;",可以看到輸出結果"// Result: EDGE 3: 3 0 Hard",其中"EDGE 3:"代表邊線(e[3]),3和0分別代表組成這條邊的兩個點(vtx[3]和vtx[0])的索引號。注意,這兩個點的順序不是按大小排列的,而是按照邊線的構造順序。

我們把polyInfo按照自己的需要封裝起來。主要是用字符處理的方法實現(xiàn)的,注意這里用到了一個前面講過的工具函數(shù)getBaseName()。你會發(fā)現(xiàn)這個函數(shù)的用途與getVerts()很像,但getVerts()無法得知邊線的構造順序。

// 根據(jù)一條邊,得到這條邊的按構造順序排列的兩個端點。
proc string[] edge2Vertex(string $edge)
{
string $verts[], $buffer[];
string $edgeInfo[] = `polyInfo -ev $edge`;
int $nbVertex = tokenize($edgeInfo[0], $buffer);

string $polyName = getBaseName($edge);
$verts[0] = $polyName + ".vtx[" + $buffer[2] + "]";
$verts[1] = $polyName + ".vtx[" + $buffer[3] + "]";
return $verts;
}
第二步,我們要找到需要切割的兩條邊。
我們可以根據(jù)選擇的一條邊,和要切割的那個面來判斷。
    選擇一條邊。Mel歷史窗中的代碼:  select -r polySurface1.e[6] ;

Edit Polygons->Selection->Convert Selection to Vertices,轉換成頂點。
[注] 這一步mel歷史窗中可能看不到變化,按z鍵undo一下就看到了。

Mel歷史窗中的代碼: ConvertSelectionToVertices;

再選擇Edit Polygons->Selection->Convert Selection to Edges,轉換成邊。

Mel歷史窗中的代碼: ConvertSelectionToEdges;

去掉開始那條邊的選擇。

Mel歷史窗中的代碼: select -tgl polySurface1.e[6] ;

[注] select -d polySurface1.e[6] ;也可。

[注] select -d polySurface1.e[6] ;也可。

現(xiàn)在剩下四條邊,可以用getSelEdges()把它們存到一個數(shù)組中。
數(shù)組1:
{"polySurface1.e[1]",
"polySurface1.e[3]",
"polySurface1.e[4]",
"polySurface1.e[5]"}

選擇要切割的面。Mel歷史窗中的代碼: select -r polySurface1.f[1] ;

用getEdges()把屬于面的四條邊存到另一個數(shù)組中。
{polySurface1.e[0],
polySurface1.e[1],
polySurface1.e[4],
polySurface1.e[6]}

用intersectStringArray()可以找到兩個數(shù)組的共同部分,就是我們將要切割的兩條邊。
{polySurface1.e[1],
polySurface1.e[4]}

用getEdges()把屬于面的四條邊存到另一個數(shù)組中。
{polySurface1.e[0],
polySurface1.e[1],
polySurface1.e[4],
polySurface1.e[6]}

用intersectStringArray()可以找到兩個數(shù)組的共同部分,就是我們將要切割的兩條邊。
{polySurface1.e[1],
polySurface1.e[4]}

把前面Mel歷史窗中記錄下的代碼整理一下,就成了:

// 已知一個面,這個面的一條邊,求與(這個面的)這條邊相鄰的兩條邊
proc string[] adjacentEdgesInFace(string $face, string $edge)
{
// 獲取所有相鄰的邊線
select -r $edge;
ConvertSelectionToVertices();
ConvertSelectionToEdges();
select -d $edge;
string $edges_vert[] = getSelEdges();

// 獲取已知面的所有邊線
select -r $face;
string $edges_face[] = getEdges();

// 求兩個數(shù)組的共同部分
string $edges[] = intersectStringArray($edges_vert, $edges_face);
return $edges;
}
第三步,切割一個面。
我們可以先把切割的百分比設置一個固定的數(shù)值,設為0.2(20%)。我們可以通過edge2Vertex()來得到要切割的一條邊
的起點和終點,如果起點恰好是當初選擇的那條邊線的一個端點(兩條邊的公共點),那么這條線的構造順序是正的,可以直接
使用20%;但如果構造順序是反的,那就要使用1-20%=80%了。
這個函數(shù)應該這么寫:

proc splitByPercent(string $edge1, string $edge2, string $inputEdge)
{
// 預設值,百分比為0.2
float $percent = 0.2;
float $percent1 = $percent; // 0.2
float $percent2 = $percent; // 0.2

// 分別獲得三條邊所包含的頂點
string $verts1[], $verts2[], $vInput[];
$vInput = edge2Vertex($inputEdge);
$verts1 = edge2Vertex($edge1);
$verts2 = edge2Vertex($edge2);

// 求$edge1與$inputEdge的公共點
string $startVert[] = intersectStringArray($verts1, $vInput);
// 如果公共點不是$edge1的起點
if ($startVert[0] != $verts1[0])
// 百分比變?yōu)?0%,即1-0.2
$percent1 = 1 - $percent;

// 求$edge2與$inputEdge的公共點
string $startVert[] = intersectStringArray($verts2, $vInput);
if ($startVert[0] != $verts2[0])
$percent2 = 1 - $percent;

// 獲得兩條邊的索引號
string $index1 = getIndex($edge1);
string $index2 = getIndex($edge2);

// 準備命令字符串
string $cmd = "polySplit -ch on -s 1 ";
$cmd += "-ep " + $index1 + " " + $percent1 + " ";
$cmd += "-ep " + $index2 + " " + $percent2 + " ";
$cmd += ";";

// 選擇整個多邊形物體
string $polyName = getBaseName($edge1);
select -r $polyName;

// 執(zhí)行命令
evalEcho($cmd);
}
[注] 使用evalEcho執(zhí)行命令可以把命令字符串在mel歷史窗中顯示出來。

第四步,切割邊線兩邊的面。

有了前面的準備工作,最后一步就顯得比較容易了。

global proc myEdgeChamfer()
{
// 獲取選擇的一條邊
string $edges[] = getSelEdges();
string $inputEdge = $edges[0];

// 獲取選擇的邊相鄰的兩個面
string $faces[] = getFaces();

// 等比切割第1個面
string $splitEdges[];
$splitEdges = adjacentEdgesInFace($faces[0], $inputEdge);
splitByPercent($splitEdges[0], $splitEdges[1], $inputEdge);

// 等比切割第2個面
$splitEdges = adjacentEdgesInFace($faces[1], $inputEdge);
splitByPercent($splitEdges[0], $splitEdges[1], $inputEdge);
}
附全部源代碼。

///////////////////////////////////////////////////////////
// myEdgeChamfer.mel
// myEdgeChamfer v1

// 獲取選擇的多邊形頂點
proc string[] getSelVerts()
{
return `filterExpand -ex true -sm 31`;
}

// 獲取選擇的多邊形邊
proc string[] getSelEdges()
{
return `filterExpand -ex true -sm 32`;
}

// 獲取選擇的多邊形面
proc string[] getSelFaces()
{
return `filterExpand -ex true -sm 34`;
}

// 根據(jù)點、邊、面、UV點的名稱得出多邊形的名稱
// 例如多邊形一條邊的名稱為"pSphere1.e[637]",則這個多邊形的
// 名稱為"pSphere1"
proc string getBaseName(string $item)
{
string $buffer[];
if ($item != "")
{
tokenize($item, ".", $buffer);
}
return $buffer[0];
}

// 根據(jù)點、邊、面、UV點的名稱得出它們的索引號
// 例如多邊形一條邊的名稱為"pSphere1.e[637]",則這個多邊形的

// 索引號為637
proc int getIndex(string $indexString)
{
string $buffer[];
tokenize($indexString, "[]", $buffer);
int $index = (int)$buffer[1];
return $index;
}

// 獲得兩個數(shù)組的共同部分
proc string[] intersectStringArray(string $array1[], string $array2[])
{
global string $m_arrayIntersector;
if ($m_arrayIntersector == "")
$m_arrayIntersector = `stringArrayIntersector`;

stringArrayIntersector -edit -intersect $array1 $m_arrayIntersector;
stringArrayIntersector -edit -intersect $array2 $m_arrayIntersector;
string $result[] = `stringArrayIntersector -query $m_arrayIntersector`;
stringArrayIntersector -edit -reset $m_arrayIntersector;
return $result;
}

///////////////////////////////////////////////////////////
// 第一步,根據(jù)一條邊,得到這條邊的按構造順序排列的兩個端點。
proc string[] edge2Vertex(string $edge)
{
string $verts[], $buffer[];
string $edgeInfo[] = `polyInfo -ev $edge`;
int $nbVertex = tokenize($edgeInfo[0], $buffer);

string $polyName = getBaseName($edge);
$verts[0] = $polyName + ".vtx[" + $buffer[2] + "]";
$verts[1] = $polyName + ".vtx[" + $buffer[3] + "]";
return $verts;
}

// 已知一個面,這個面的一條邊,求與(這個面的)這條邊相鄰的兩條邊
proc string[] adjacentEdgesInFace(string $face, string $edge)
{
// 獲取所有相鄰的邊線
select -r $edge;
ConvertSelectionToVertices();
ConvertSelectionToEdges();
select -d $edge;
string $edges_vert[] = getSelEdges();

// 獲取已知面的所有邊線
select -r $face;
string $edges_face[] = getEdges();

// 求兩個數(shù)組的共同部分
string $edges[] = intersectStringArray($edges_vert, $edges_face);
return $edges;
}

// 第三步,等比切割一個面
proc splitByPercent(string $edge1, string $edge2, string $inputEdge)
{
// 預設值,百分比為0.2
float $percent = 0.2;
float $percent1 = $percent; // 0.2
float $percent2 = $percent; // 0.2

// 分別獲得三條邊所包含的頂點
string $verts1[], $verts2[], $vInput[];
$vInput = edge2Vertex($inputEdge);
$verts1 = edge2Vertex($edge1);
$verts2 = edge2Vertex($edge2);

// 求$edge1與$inputEdge的公共點
string $startVert[] = intersectStringArray($verts1, $vInput);
// 如果公共點不是$edge1的起點
if ($startVert[0] != $verts1[0])
// 百分比變?yōu)?0%,即1-0.2
$percent1 = 1 - $percent;

// 求$edge2與$inputEdge的公共點
string $startVert[] = intersectStringArray($verts2, $vInput);
if ($startVert[0] != $verts2[0])
$percent2 = 1 - $percent;

// 獲得兩條邊的索引號
string $index1 = getIndex($edge1);
string $index2 = getIndex($edge2);

// 準備命令字符串
string $cmd = "polySplit -ch on -s 1 ";
$cmd += "-ep " + $index1 + " " + $percent1 + " ";
$cmd += "-ep " + $index2 + " " + $percent2 + " ";
$cmd += ";";

// 選擇整個多邊形物體
string $polyName = getBaseName($edge1);
select -r $polyName;

// 執(zhí)行命令
evalEcho($cmd);
}

// 第四步,切割選擇的一條邊線兩邊的面。
global proc myEdgeChamfer()
{
// 獲取選擇的一條邊
string $edges[] = getSelEdges();
string $inputEdge = $edges[0];

// 獲取選擇的邊相鄰的兩個面
string $faces[] = getFaces();

// 等比切割第1個面
string $splitEdges[];
$splitEdges = adjacentEdgesInFace($faces[0], $inputEdge);
splitByPercent($splitEdges[0], $splitEdges[1], $inputEdge);

// 等比切割第2個面
$splitEdges = adjacentEdgesInFace($faces[1], $inputEdge);
splitByPercent($splitEdges[0], $splitEdges[1], $inputEdge);
}


myEdgeChamfer_v1.rar (1.46k)

等距切割

要想知道一條邊的長度,如果是Maya5.0或更低版本,可以使用arclen命令,但是到了Maya6.0,arclen命令只支持Nurbs,退化了?

這樣我們不得不用到獲得兩點之間的距離的功能來計算邊的長度,F(xiàn)在要提到一點圖形學知識,當然是最最基礎的,就是"勾股定理"。相信大家都能記得一點"勾股定理"的內容,我就不詳細講了。如果需要詳細講的話,可以提出來,我可以在后面補充。

"勾股定理"的公式是: a2 + b2 = c2

根據(jù)這個公式推理出3D空間勾股定理公式:x2 + y2 + z2 = d2

如果求兩點之間的距離,公式如圖:dist為點P1(x1,y1,z1)和P2(x2,y2,z2)之間的距離

根據(jù)公式,我們來編一個工具函數(shù)求兩點之間的距離。你也許會感到奇怪,這么常用的功能,Maya中為什么沒有內置的命令呢?這一點我也感到奇怪,好在編寫這樣的功能非常簡單。

// 計算兩個頂點之間的距離
proc float distance2Verts(string $vert1, string $vert2)
{
// 獲取兩個頂點的坐標值
float $p1[] = `pointPosition $vert1`;
float $p2[] = `pointPosition $vert2`;

// 計算距離
float $distance;
float $v[3];
$v[0] = $p1[0] - $p2[0];
$v[1] = $p1[1] - $p2[1];
$v[2] = $p1[2] - $p2[2];
$distance = $v[0]*$v[0] + $v[1]*$v[1] + $v[2]*$v[2];
$distance = sqrt($distance);// 開方

return $distance;
}


[注] 獲取點的坐標值還有一種方法是:
float $p1[] = `xform -q -ws -t $ver1`;


[注] 獲取點的坐標值還有一種方法是:
float $p1[] = `xform -q -ws -t $ver1`;

現(xiàn)在只要把等比切割的函數(shù)改一改就差不多了,把splitByPercent()改為splitByDist()。我把沒有改動的部分用灰綠色表示,重點看看改動的部分。
[注] dist為distance的縮寫。

// 第三步,等距切割一個面
proc splitByDist(string $edge1, string $edge2, string $inputEdge)
{
// 預設值,距離為0.2個單元格
float $dist = 0.2;
float $percent1;
float $percent2;

// 分別獲得三條邊所包含的頂點
string $verts1[], $verts2[], $vInput[];
$vInput = edge2Vertex($inputEdge);
$verts1 = edge2Vertex($edge1);
$verts2 = edge2Vertex($edge2);


// 計算第一條邊的切割處
float $edgeLength; // 求第一條邊的長度
$edgeLength = distance2Verts($verts1[0], $verts1[1]);
if ($edgeLength < $dist) // 如果長度小于預設值0.2
$percent1 = 1; // 切割處在線的的末端
else
$percent1 = $dist / $edgeLength; // 計算出百分比

// 計算第二條邊的切割處
$edgeLength = distance2Verts($verts2[0], $verts2[1]);
if ($edgeLength < $dist)
$percent2 = 1;
else
$percent2 = $dist / $edgeLength;

// 求$edge1與$inputEdge的公共點
string $startVert[] = intersectStringArray($verts1, $vInput);
// 如果公共點不是$edge1的起點
if ($startVert[0] != $verts1[0])

// 百分比變?yōu)?-$percent1
$percent1 = 1 - $percent1;

// 求$edge2與$inputEdge的公共點
string $startVert[] = intersectStringArray($verts2, $vInput);
if ($startVert[0] != $verts2[0])

$percent2 = 1 - $percent2;

// 獲得兩條邊的索引號
string $index1 = getIndex($edge1);
string $index2 = getIndex($edge2);

// 準備命令字符串
string $cmd = "polySplit -ch on -s 1 ";
$cmd += "-ep " + $index1 + " " + $percent1 + " ";
$cmd += "-ep " + $index2 + " " + $percent2 + " ";
$cmd += ";";

// 選擇整個多邊形物體
string $polyName = getBaseName($edge1);
select -r $polyName;

// 執(zhí)行命令
evalEcho($cmd);

}

把distance2Verts()函數(shù)的定義加進去,再把myEdgeChamfer()函數(shù)中的兩處splitByPercent()改為splitByDist(),這個程序就完成了。

附全部源代碼。

myEdgeChamfer_v2.rar (1.74k)

學習 · 提示

  • 一定要打開PS,跟著教程做一遍,做完的圖到這交作業(yè):提交作業(yè)
  • 建議練習時,大家自己找素材,盡量不要用教程提供的素材。
  • 教程有看不懂的地方,可以到論壇發(fā)帖提問:新手求助
  • 加官方微信,隨時隨地,想學就能學:ps_bbs,或掃右側二維碼!
  • 關注我們學更多,每天都有新教程:新浪微博 抖音視頻 微信小程序
- 發(fā)評論 | 交作業(yè) -
最新評論
暫無評論,交個作業(yè)支持一下吧~

關注大神微博加入>>

網(wǎng)友求助,請回答!