Tuesday, July 20, 2010

Maya에서 follicle 이용해서 object를 surface에 attach하는 법

질문 : Maya에서 object를 surface에 attach하는 법?
(예를 들어 T-shirt에 button 단추 다는 등)

답 :
Follicle 노드가 현재로서는 유일하게 UV에 부착되는 노드이므로 follicle을 쓰는 것 외의 방법은 모두 편법에 불과하다. 그런데 Follicle을 그냥 쓰려고 하면 hair를 생성할때 follicle의 위치가 random으로 생성되어 컨트롤할 수 없다. 따라서 다음 MEL을 쓰는 것이 정답.

사용법 :
1. attach할 object 선택
2. attach할 surface 선택
3. parentToSurface 실행

출처 : http://www.kylenikolich.com/scripting/lod/parentToSurface.mel

주의점 : 오른쪽과 왼쪽이 대칭으로 같은 texture 를 공유하거나 하는 경우 UV space 를 공유하게 되므로 이 MEL로 follicle을 제대로 심을 수가 없다. (왼쪽에 follicle을 심으려 했는데 왼쪽에 심어지는 식이 돼서 엉망이 됨)

parentToSurface.mel 의 내용 :

// parentToSurface

// This mel command allows one to attach selected objects to a selected mesh or nurbs surface.
// The objects will follow any deformation or transformation of the surface.

// Usage: put this script in your local scripts directory. In Maya select object(s) to attach
// followed by a mesh or nurbs surface to attach then enter "parentToSurface" in the
// command line. A follicle node will be created at the point on surface closest to
// the center of the object and the object will be parented to this follicle. Note that
// if the surface to attach to is a mesh it must have well defined UVs that range from 0-1
// with no areas sharing the same value.
//
// For convenience drag the parentToSurface string onto the shelf to make a shelf button.
//
// This command uses the follicle node, which is normally used by the hair system. The follicle node
// is currently the only node in maya that can derive a rotate and translate based on a uv position
// for both meshes and nurbs surfaces.
//
// One use of this script might be to attach buttons to a cloth object, or any deforming surface. To
// attach several buttons, first position the buttons where desired then select them followed by the
// object to attach to and run this command.
// For more info or to report problems with this script go to Duncan's Corner:
// http://area.autodesk.com/blogs/blog/7/

global proc float convertToCmFactor()
{
string $unit = `currentUnit -q -linear`;
if( $unit == "mm" ){
return( 0.1 );
} else if( $unit == "cm" ){
return( 1.0 );
} else if( $unit == "m" ){
return( 100.0 );
} else if( $unit == "in" ){
return( 2.54 );
} else if( $unit == "ft" ){
return( 30.48 );
} else if( $unit == "yd" ){
return( 91.44 );
} else {
return( 1.0 );
}
}

global proc attachObjectToSurface(string $obj, string $surface, float $u, float $v )
{
string $follicle = `createNode follicle`;
string $tforms[] = `listTransforms $follicle`;
string $follicleDag = $tforms[0];


connectAttr ($surface + ".worldMatrix[0]") ($follicle + ".inputWorldMatrix");
string $nType = `nodeType $surface`;
if( "nurbsSurface" == $nType ){
connectAttr ($surface + ".local") ($follicle + ".inputSurface");
} else {
connectAttr ($surface + ".outMesh") ($follicle + ".inputMesh");
}
connectAttr ($follicle + ".outTranslate") ($follicleDag + ".translate");
connectAttr ($follicle + ".outRotate") ($follicleDag + ".rotate");
setAttr -lock true ($follicleDag + ".translate");
setAttr -lock true ($follicleDag + ".rotate");
setAttr ($follicle + ".parameterU") $u;
setAttr ($follicle + ".parameterV") $v;

//parent -addObject -shape $obj $follicleDag;
parent $obj $follicleDag;
}

global proc parentToSurface()
{
string $sl[] = `ls -sl`;
int $numSel =size($sl);
if( $numSel < 2 ){ warning( "ParentToSurface: select object(s) to parent followed by a mesh or nurbsSurface to attach to."); return; } string $surface = $sl[$numSel-1]; if( nodeType($surface) == "transform" ){ string $shapes[] = `ls -dag -s -ni -v $surface`; if( size( $shapes ) > 0 ){
$surface = $shapes[0];
}
}
string $nType = `nodeType $surface`;
if( $nType != "mesh" && $nType != "nurbsSurface"){
warning( "ParentToSurface: Last selected item must be a mesh or nurbsSurface.");
return;
}
string $clPos = "";
float $minU, $minV, $sizeU, $sizeV;
float $convertFac = 1.0;

if( $nType == "nurbsSurface" ){
$clPos = `createNode closestPointOnSurface`;
connectAttr ($surface + ".worldSpace[0]") ($clPos + ".inputSurface");

$minU = `getAttr ($surface+".mnu")`;
float $maxU = `getAttr ($surface+".mxu")`;
$sizeU = $maxU - $minU;
$minV = `getAttr ($surface+".mnv")`;
float $maxV = `getAttr ($surface+".mxv")`;
$sizeV = $maxV - $minV;
} else {
int $pomLoaded = `pluginInfo -query -l nearestPointOnMesh`;
if( !$pomLoaded ){
loadPlugin nearestPointOnMesh;
$pomLoaded = `pluginInfo -query -l nearestPointOnMesh`;
if( !$pomLoaded ){
warning( "ParentToSurface: Can't load nearestPointOnMesh plugin.");
return;
}
}
// The following is to overcome a units bug in the nearestPointOnMesh plugin
// If at some point it correctly handles units, then we need to take out the
// following conversion factor.
$convertFac = convertToCmFactor();

$clPos = `createNode nearestPointOnMesh`;
connectAttr ($surface + ".worldMesh") ($clPos + ".inMesh");
}

int $i;
float $closestU, $closestV;
for( $i = 0; $i < $numSel -1; $i++ ){
string $obj = $sl[$i];
if( nodeType( $obj )!= "transform" ){
warning( "ParentToSurface: select the transform of the node(s) to constrain\n");
continue;
}
float $bbox[] = `xform -q -ws -bb $obj`;
float $pos[3];
$pos[0] = ($bbox[0] + $bbox[3])*0.5;
$pos[1] = ($bbox[1] + $bbox[4])*0.5;
$pos[2] = ($bbox[2] + $bbox[5])*0.5;
setAttr ($clPos + ".inPosition") -type double3
($pos[0]*$convertFac)
($pos[1]*$convertFac)
($pos[2]*$convertFac);
$closestU = getAttr( $clPos + ".parameterU");
$closestV = getAttr( $clPos + ".parameterV");
if( $nType == "nurbsSurface" ){
$closestU = ($closestU + $minU)/$sizeU;
$closestV = ($closestV + $minV)/$sizeV;
}

attachObjectToSurface( $obj, $surface, $closestU, $closestV );
}


if( $clPos != "" ){
delete $clPos;
}

}

parentToSurface;

기타 다른 방법: 다음과 같이 Attach to Motion Path 를 쓰는 방법(Object to Surface Constraints in Maya)도 있긴 하지만 실제로 써보면 속도가 너무 느려져서 (불필요한 연산이 많이 수행되는듯) 사용하기 안좋다. Play시간이 엄청 길어지는데, 멀쩡히 돌아가던 마야가 거의 멎는 수준.

No comments:

Post a Comment