/*
naMirrorUV.mel v1.1.0
Purpose: Mirror Texture Map
Author: Nathaniel Anozie
ogbonnawork at gmail dot com
nathanielanozie dot blogspot dot com
Acknowledgement: Bryan Ewert (xyz2 dot net), where i learned that one vertex could map to lots of uvs
Tested on 4000 vtx character in Maya 2008
To use Mirror Texture Map
-source 'naMirrorUV.mel'
-select uv-shell to mirror
-type 'naMirrorTextureMap()'
*/
/////given a vertex return the mapped vertex faces
/*
in uv texturing, not all vertex faces are mapped, mapping may be left till later
this takes care of finding which vertex faces are mapped/with uv
*/
global proc string[] getMappedVertexFaceArrayFromVertex(string $vtx)
{
string $result[];
string $vertexFaces[] = getVertexFaceArrayFromVertex($vtx);
for($i=0; $i<size($vertexFaces); $i++)
{
string $uv[] = `polyListComponentConversion -fvf -tuv $vertexFaces[$i]`;
if(size($uv) == 1){
$result[size($result)] = $vertexFaces[$i];
}
}
return $result;
}
/////given a vertex get the vertex faces for it all separated returns and empty array if nothing found
/*
used for uv mapping, since a vertex can be associated with multple faces
there should be one to one mapping between vertex face and a uv and
not a vertex and a uv
*/
global proc string[] getVertexFaceArrayFromVertex(string $vtx)
{
string $sel[] = `ls -sl`;
string $result[];
string $vtxFaceArrayRangeCondensed[] = `polyListComponentConversion -fv -tvf $vtx`;
if(size($vtxFaceArrayRangeCondensed) > 0)
{
select -cl;
select -r $vtxFaceArrayRangeCondensed;
$result = `filterExpand -sm 70 -expand true`;
select -r $sel;
}
return $result;
}
/////given vertex face return 1 if has uv, 0 if doesn't, and empty array otherwise
/*
in uv texturing, to help with knowing if a vertex face has a mapped uv
*/
global proc int[] isVtxFaceWithUV(string $_vtxFace)
{
int $result[];
string $vtxFace = $_vtxFace;
$uvVertex = `polyListComponentConversion -fvf -tuv $vtxFace`;
if(size($uvVertex) > 0)
{
if(size($uvVertex) == 1)
{
$result[0] = 1;
}
else
{
if(size($uvVertex) == 0)
{
$result[0] = 0;
}
}
}
return $result;
}
/////get mapped vtxFaces given selected and mirrored vertices no vertex faces excluded
/////first element can be a selected vertex or a mirrored vertex (check this)
/*
because automatic mapping and cylindrical mapping map vertex faces differently
i ran into two mapped (mirrored) vertices with identical number of vertex faces
having different number of vertex faces mapped
*/
global proc string[] getMappedVtxFaces(string $arg, string $selVtx, string $mirrorVtx)
{
string $result[];
//get all mapped faces for mirrored vertex
string $mirroredVtxFacesWithMap[] = getMappedVertexFaceArrayFromVertex($arg);
//take away those vfaces we dont have mapped for both selected and mirrored vface
int $endArray[] = getMaxNumberVtxFacesMappedForSelectedAndMirror($selVtx, $mirrorVtx);
if(size($endArray)>0){
int $end = $endArray[0];
for($i=0; $i<$end; $i++){ $result[size($result)] = $mirroredVtxFacesWithMap[$i];}
}
return $result;
}
/////
/*
because automatic mapping and cylindrical mapping map vertex faces differently
i ran into two mapped (mirrored) vertices with identical number of vertex faces
having different number of vertex faces mapped
here we take the min number of both and return that number
*/
global proc int[] getMaxNumberVtxFacesMappedForSelectedAndMirror(string $selVtx, string $mirrorVtx)
{
int $result[];
//get all mapped faces for selected vertex
string $selectedVtxFacesWithMap[] = getMappedVertexFaceArrayFromVertex($selVtx);
string $mirroredVtxFacesWithMap[] = getMappedVertexFaceArrayFromVertex($mirrorVtx);
int $min = min(size($selectedVtxFacesWithMap),size($mirroredVtxFacesWithMap));
if($min >= 0){ $result[0] = $min; }
return $result;
}
/////given lots of vertices selected cut the uv shell of it
/*
in uv mapping to help texture artist, mirrored shell should
be cut up so that the individual uvs can be moved
*/
global proc cutUVShellFromVtxSelection(string $vtxArray[])
{
string $edges[] = `polyListComponentConversion -fv -te $vtxArray`;
if(size($edges) > 0){
polyMapCut -ch 1 $edges;
}
}
/////given one/or more user selected uv-shells, made through the uv texture editor mirror the uv-shell
/*
user can select individual uv's but enough have to be selected to make up at least one face
*/
global proc naMirrorTextureMap()
{
string $sel[] = `ls -sl`;
//if user selected a shell then
//this wont be empty
string $mapSelected[] = `filterExpand -sm 35 -expand true`;
//make sure we have an actual map selected
if(size($mapSelected) > 0){
//get vertices selected by using mask for vertices to get expanded form of selection
string $vertsSelectedShort[] = `polyListComponentConversion -fuv -tv $mapSelected`;
//many of these vertices may be involved in
//multiple uv shells and later
//mirroring would cause mirroring of uv's that are not part of the shell we want mirrored
//i think mirroring vertex faces would be a better idea (check this)
select -cl;
select -r $vertsSelectedShort;
string $expandedVSel[] = `filterExpand -sm 31 -expand true`;
select -r $sel;
//by making reselection but this time vertices we ensure mirror vertices correspond to selected vertex
string $vertsSelected[] = $expandedVSel;
select -cl;
select -r $vertsSelected;
string $mirroredVerts[] = naMirrorSelectedVerts();
if(size($mirroredVerts) > 0 ){
//this is where we do the auto uv maps for the mesh on opposite side of selected
//that side should probably not have any uvs already, this doesn't check that to make sure
//Then we cut the map, after cut ex: vtx 1 (whose original value is vtx 92) could link to many uvs uv 5, 7, 9
//where to move uv 5,7,9, we move them using vertex faces
//no other vertices should be involved in projection
//other than the mirrored vertices
string $polyName[] = getPolyFromComponent($sel[0]);
string $allVtx[] = getVtxFromPoly($polyName[0]);
select -r $allVtx;
string $vertsNonMirror[] = stringArrayRemove( $mirroredVerts, $allVtx);
//for automapping the opposite of user's mesh
//make sure no overlap faces between mirror and selected
string $autoMappedFaces[];
$autoMappedFaces = faceArrayRemoveFromVtx($vertsNonMirror,$mirroredVerts);
if(size($autoMappedFaces) > 0){
polyAutoProjection -ch 0 -lm 0 -pb 0 -ibd 1 -cm 0 -l 2 -sc 1 -o 1 -p 6 -ps 0.2 -ws 1 $autoMappedFaces;
}
//uvs can be moved individually without having ovewrapping because
//lots of possible clumping for automap
cutUVShellFromVtxSelection($mirroredVerts);
//loop verts of selected
int $i=0;
//check speed
//3 functions called with every vertex, these functions sometimes call other functions
//moveUVToMirrorPosition
//getSelectedVtxFaces //calls getMappedVertexFaceArrayFromVertex, getMaxNumberVtxFacesMappedForSelectedAndMirror
//getMirrorVtxFaces //calls getMappedVertexFaceArrayFromVertex, getMaxNumberVtxFacesMappedForSelectedAndMirror
string $movedUV[];//will tell us which uvs were in fact moved by code
/*
we need this because automap may add uvs that we cannot move, so we want to remove these
from the texture editor.
*/
for($i=0; $i<size($vertsSelected); $i++)
{
string $selVtx = $vertsSelected[$i];
string $mirrorVtx = $mirroredVerts[$i];
/////from two vertices, one on one side of mesh, and another a mirror of it
/////this tries to move the uv's of one side to match the uv's user made on the other side
//moveUVToMirrorPosition($vertsSelected[$i],$mirroredVerts[$i]);
/*
we are going to move a uv in the uv coordinate space
we are going to move each uv associated with a vertex face --check this
this is a brief example
weve got uv map 5, map 9 , map 12 the user mapped already
weve got uv map 3, map 2, map 24 as the code auto mapped part that should match with user part
this moves in uv coordinates, all the code automapped uvs to corresponding user mapped uvs
*/
/*
//get all mapped faces for selected vertex that has corresponding for mirrored vertex
//im confused shouldn't everything be mapped, probably can just put a vtx to face conversion
//maybe this is useful if something went wrong in auto map part but i dont think that is likely
*/
/*
//why do we go vtx-vtxface-map
//and not vtx - map
*/
//string $selectedVtxFacesWithMap[] = getMappedVertexFaceArrayFromVertex($selVtx);
//string $mirroredVtxFacesWithMap[] = getMappedVertexFaceArrayFromVertex($mirrorVtx);
string $selectedVtxFacesWithMap[] = getMappedVtxFaces($selVtx, $selVtx, $mirrorVtx);
string $mirroredVtxFacesWithMap[] = getMappedVtxFaces($mirrorVtx, $selVtx, $mirrorVtx);
///i want to see if went directly from vtx to uv what would we get
//string $uvFromSelectedVtx[] = `polyListComponentConversion -fv -tuv $selVtx`;
//string $uvFromMirrorVtx[] = `polyListComponentConversion -fv -tuv $mirrorVtx`;
///
//most of the time we probably will be mirroring across the u or horizontal direction in map space
//here is where we say that if wanted to mirror across v we would use {1,-1} for mirror direction
float $mapMirrorDirection[] = {-1,1};
//for non perfect mirror and or/ mirrored faces not mapped
//or gaps mesh these may not be equal so we check that
//mirrored and selected vertex have same number of vertex faces
if(size($selectedVtxFacesWithMap) == size($mirroredVtxFacesWithMap) ){
//loop selected vface
int $i=0;
string $mirroredNameUV[];
string $selectedNameUV[];
for($i=0; $i<size($selectedVtxFacesWithMap); $i++)
{
//do the moving in coordinate system for mirrored item
$mirroredNameUV = `polyListComponentConversion -fvf -tuv $mirroredVtxFacesWithMap[$i]`;
$selectedNameUV = `polyListComponentConversion -fvf -tuv $selectedVtxFacesWithMap[$i]`;
float $uvPos[] = `polyEditUV -query $selectedNameUV[0]`;
float $uPos = ($mapMirrorDirection[0])*$uvPos[0];
float $vPos = ($mapMirrorDirection[1])*$uvPos[1];
//if we are using user selected uv shell
//and not other uved uv shells that user didn't select
//we go ahead and move
//default to assume uv is not from user selected shell
int $isUVFromUserSelectedShell = 0;
$isUVFromUserSelectedShell = stringArrayContains($selectedNameUV[0],$mapSelected);
if( $isUVFromUserSelectedShell == 1 ){
//movement
polyEditUV -r false -u $uPos -v $vPos $mirroredNameUV[0];
//save moved uv
$movedUV[size($movedUV)] = $mirroredNameUV[0];
}//end if check for uv
}
//end loop vface
}
else
{
error("cannot mirror uvs, please check mesh placement and/or vtx positions...");
}
}//end vertex loop
////remove extra uvs automapped by code for cleanup
//first select all automapped mirrored vtx faces uvs
string $autoMappedUV[] = `polyListComponentConversion -ff -tuv $autoMappedFaces`;
select -cl;
select -r $autoMappedUV;
//now we want selected to be the non moved uvs
//we use toggle so we unselect what was already selected
select -tgl $movedUV;
//we are left with uvs that were not moved and we wish to delete
string $unMovedUVMirror[] = `ls -sl`;
//need this check the function does not except empty arguments
if(size($unMovedUVMirror) > 0){
polyMapDel -ch 0 $unMovedUVMirror;
}
//merge uvs for cleanup
string $uvMirror[] = `polyListComponentConversion -fv -tuv $mirroredVerts`;
//no construction history for speed increase
polyMergeUV -d 0.01 -ch 0 $uvMirror;
}
}//end if
else{
print("please select a uv-shell");
}
//restore user selection
select -r $sel;
}
/////remove vertex items (faces) from the second string array
/*
here is a helpful tool to find what faces of input is shared and remove the shared faces from the returned faces
*/
global proc string[] faceArrayRemoveFromVtx(string $item[],string $list[])
{
string $sel[] = `ls -sl`;
//return saveFaces minus removeFaces
string $result[];
string $saveFaces[];
string $removeFaces[];
//convert vertex input into faces
string $saveExpandedFaces[] = `polyListComponentConversion -fv -tf $list`;
select -cl;
select -r $saveExpandedFaces;
$saveFaces = `filterExpand -sm 34 -expand true`;
select -r $sel;
//print "save faces";
//print $saveFaces;
//get what faces should be removed
string $removeExpandedFaces[] = `polyListComponentConversion -fv -tf $item`;
select -cl;
select -r $removeExpandedFaces;
$removeFaces = `filterExpand -sm 34 -expand true`;
select -r $sel;
if(size($saveFaces) > 0){
if( size($removeFaces) > 0){
//find what faces of input is shared and remove the shared faces from the returned faces
string $intersector = `stringArrayIntersector`;
stringArrayIntersector -edit -intersect $saveFaces $intersector;
stringArrayIntersector -edit -intersect $removeFaces $intersector;
string $shareOneOrMoreVtxFaces[] = `stringArrayIntersector -query $intersector`;
deleteUI $intersector;
$result = stringArrayRemove( $shareOneOrMoreVtxFaces, $saveFaces);
}
else{
//dont remove any faces
$result = $saveFaces;
}
}
return $result;
}
//Used for finding nodes like skinning blendshape etc
//for getting selections like get vertices from a polygon selection
//Last Modified: Nov 23, 2011
//By: Nathaniel Anozie ogbonnwork at gmail dot com
////given string array of node and node type return array for name first occurence of node type
/*
/////
string array of node
ex: [blendNode, tweakNode, shapeNode]
string node type: tweak, blendShape
/////
*/
global proc string[]
getFirstHasNodeType(string $nodeArray[], string $nodeType)
{
string $result[];
int $i= 0;
string $tempType;//store node type of loop element,cleared on every step
//loop nodes of input
for ($i=0;$i< size($nodeArray); $i++)
{
$tempType = `nodeType $nodeArray[$i]`;
//if found matching node return it
if( $tempType == $nodeType )
{
$result[0] = $nodeArray[$i];
break;
}
}
return $result;
}
/////return vertices given a polygon selection
/*
often user may need to user vertices from a polygon selection
this script helps with returning that
*/
global proc string[]
getVtxFromPoly(string $poly)
{
string $result[];
string $sel[] = `ls -sl`;
//will be altering user scene
//with constaint command
//so added check necessary
if(`objExists $poly`){
print "hii";
select -cl;
select -r $poly;
polySelectConstraint -type 0x0001 -shell true -m 3;//mode no constraint used
string $expanded[] = `ls -sl`;
$result = `filterExpand -sm 31 -expand true`;
polySelectConstraint -shell false;
select -cl;
select -r $sel;
}
else
{
warning("no poly exists");
}
return $result;
}
/*
naMirrorSelectedVerts.mel v1.0.1
Last Modified: November 14, 2011
Author: Nathaniel Anozie
ogbonnawork at gmail dot com
nathanielanozie dot blogspot dot com
Purpose: Mirror selected vertices
Supports Low Poly Meshes ex < 10000 verts on a side
Tested on mirroring about 9000 verts which took about 150 seconds
*/
/////given selected vertices return list of thier mirrored vertex
/*
//assumes nearestPointOnMesh plugin loaded
*/
global proc string[] naMirrorSelectedVerts()
{
print(`date -time`+"\n");
string $_verts[] = `ls -sl`;
string $mVerts[]; //for each input vtx corresponding mirrored vtx
if(size($_verts)>0){
//string $verts[] = expandComponentArray($_verts);
string $verts[] = `filterExpand -sm 31 -expand true`;
string $nearestFace[]; //nearest face to each mirrored vtx
int $mDirect[] = {-1,1,1}; //mirror in x direction
//get shape for mesh that has these verts
string $polyNameArray[] = getPolyFromComponent($verts[0]);
string $polyName = $polyNameArray[0];
string $shapes[] = `listRelatives - shapes $polyName`;
string $shape = $shapes[0];
//this will be able to tell given a point what is nearest face to it
string $near = "nearNodeForNAMirrorUV";
createNode nearestPointOnMesh -n $near;
connectAttr -f ($shape+".outMesh") ($near+".inMesh");
int $i = 0;
//loop over verts
for($i=0; $i<size($verts); $i++){
float $selPos[] = `xform -q -t -ws $verts[$i]`;
//get mirrored position for each slected vtx
float $mPosX = $mDirect[0]*$selPos[0];
float $mPosY = $mDirect[1]*$selPos[1];
float $mPosZ = $mDirect[2]*$selPos[2];
//we give it the point
setAttr ($near+".inPositionX") ($mPosX);
setAttr ($near+".inPositionY") ($mPosY);
setAttr ($near+".inPositionZ") ($mPosZ);
//we get the mirrored face
string $nearestFaceId = `getAttr $near (".nearestFaceIndex")`;
$nearestFace[$i] = $polyName+".f"+"["+$nearestFaceId+"]";
string $mirrorVtx[] = getClosestVertToPos($nearestFace[$i],{$mPosX,$mPosY,$mPosZ});
$mVerts[$i] = $mirrorVtx[0];
//print($mVerts[$i]);
}
//end loop
//delete created nodes
delete($near);
select -r $_verts;
}
print(`date -time`+"\n");
return($mVerts);
}
/////given face and a world position get the closest vertex of face to that position
/*
string name of face ex: "pCube1.f[2]"
float 3 element array ex: {0.5,0.5,0.5}
*/
global proc string[] getClosestVertToPos(string $_face, float $wpos[] )
{
string $result[];
string $face = $_face;
string $vertsOfFace[];
string $_vertsOfFace[] = `polyListComponentConversion -ff -tv $face`;//has [2:3] possibly
$vertsOfFace = expandComponentArray($_vertsOfFace);
if(size($wpos)==3){
string $minVertex = $vertsOfFace[0];
float $minWorldPos[] = `xform -q -t -ws $minVertex`;
float $minValue[] = euclidDistance( $minWorldPos, $wpos );
//loop verts of face
int $i=0;
string $_minVertex="";
float $_minWorldPos[];
float $_minValue[];
for($i=0; $i<size($vertsOfFace); $i++)
{
$_minVertex = $vertsOfFace[$i];
$_minWorldPos = `xform -q -t -ws $_minVertex`;
$_minValue = euclidDistance( $_minWorldPos, $wpos );
//if found a closer point to input position save it
if( $_minValue[0] < $minValue[0] )
{
$minVertex = $_minVertex;
$minWorldPos = $_minWorldPos;
$minValue = $_minValue;
}
//end if
clear($_minWorldPos);
clear($_minValue);
$_minVertex="";
}
//end loop
$result[0] = $minVertex;
}
return $result;
}
///// given a string array of form name[int:int] unfold name[2:3] into name[2] and name[3]
/*
/////
needs string name array of form name[int:int]
*/
global proc string[] expandComponentArray(string $_verts[])
{
string $result[];
string $verts[] = $_verts;
//get expanded form of vertex[int] instead of vertex[int:int]
int $v = 0;
string $expanded[];
for($v=0; $v<size($verts); $v++)
{
$expanded = expandComponent($verts[$v]);
if(size($expanded)>0){
$result = stringArrayCatenate($result,$expanded);
}
//if found [int] add that with no change
else{
string $append[];
$append[0] = $verts[$v];
$result = stringArrayCatenate($result,$append);
}
}
return $result;
}
///// given a string of form name[int:int] unfold name[2:3] into name[2] and name[3]
/*
/////
needs string name of form name[intA:intB], intA < intB
*/
global proc string[] expandComponent( string $_arg )
{
string $result[];
string $partsNoSemiColon[];
tokenize($_arg,":",$partsNoSemiColon);
//will unfold name[2:3] into name[2] and name[3]
if(size($partsNoSemiColon) == 2)
{
string $parts[];
tokenize($_arg,"[",$parts);
string $polyName = $parts[0];
string $vertsWithBracket = $parts[1];
//now have '[int' and 'int]'
string $singleBracket[];
tokenize($vertsWithBracket,":",$singleBracket);
string $startParts[];
string $endParts[];
tokenize($singleBracket[0],"[",$startParts);
tokenize($singleBracket[1],"]",$endParts);
int $start=$startParts[0]; //int
int $end=$endParts[0]; //int
//loop start to end adding to result
int $counterInBracket=0;
string $vertex[];
for($counterInBracket = 0; $counterInBracket <= ($end-$start); $counterInBracket++)
{
//counter 0,1,2... if start is 5 end is 8 we put in vertex [5+0],[5+1], ...[5+3] or [5],[6],[7],[8]
$vertex[0] = $polyName+"["+($start+$counterInBracket)+"]";
$result = stringArrayCatenate($result,$vertex);
}
}
return $result;
}
////get poly name given any poly component
/*supports any component vtx,face,edge,vFace ..
*/
global proc string[] getPolyFromComponent(string $component)
{
string $result[];
string $parts[];
tokenize($component,".",$parts);
$result[0] = $parts[0];
return $result;
}
/////given two vectors return euclidean distance
/*
this is used to find closeness on a mesh
suppose you wanted to know of the vertices of a face
which one is closest this other point, user can
use this to find out
/////
needs two float arrays of 3 elements
/////
*/
global proc float[] euclidDistance(float $a[], float $b[] )
{
float $result[];
if( (size($a)==3) && (size($b)==3) )
{
$result[0] = sqrt( pow(($a[0]-$b[0]),2)+
pow(($a[1]-$b[1]),2)+
pow(($a[2]-$b[2]),2)
);
}
return $result;
}