// Copyright (C) 1997-2004 Alias Systems Corp.
// 
// The information in this file is provided for the exclusive use of the
// licensees of Alias.  Such users have the right to use, modify,
// and incorporate this code into other products for purposes authorized
// by the Alias license agreement, without fee.
// 
// ALIAS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
// EVENT SHALL ALIAS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

//
//
//
// ALIAS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
// EVENT SHALL ALIAS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
//
// This allows me to use some convenience procedures that
// have been predefined.
//
includeEffectsGlobals();
includeShatterEffectsGlobals();


proc smoothShardEdges( string $object, int $shardCount, int $shardIndexList[], 
					   string $shardEdgeList[], int $verbose )
//
// This procedure smooths the edges of the shards by placing each facets in a shard that
// it has the most edges in common with.
//
{
	string $face;
	string $faceList[];
    int    $faceCount;
	string $edgeList[];
	string $faceEdgeList[];
	string $edgeFaceList[];
	int    $edgeFaceCount;
	int    $faceEdgeCount;
	int    $edgeCount;
	int    $shardIndex;
	int    $faceIndex;
	int    $indexList[];


	if ($verbose == true )
	{
		print( "// Smoothing shard edges.\n" );
	}

	// For each shards edge list, determine the shard the bourder faces
	// should be in.
	//
	int $shardEdgeCount = size( $shardEdgeList );

	for ( $i = 0; $i < $shardEdgeCount; $i++ )
	{
		// Expand the edge list into individual edges.
		//
		if ( $shardEdgeList[ $i ] == "" )
		{
			continue;
		}

		// Separate the shard edge list into individual edges.
		//
		tokenize( $shardEdgeList[$i], " ", $edgeList );
		$edgeCount = size( $edgeList );

		// For each shards edge, process the faces along it.
		//
		for ( $j = 0; $j < $edgeCount; $j++ )
		{
			$faceList  = getFaceList( $edgeList[ $j ] );
			$faceCount = size( $faceList );

			// For each face, create an edge list and determine which shard is 
			// shares the most edges with.
			//
			for ( $k = 0; $k < $faceCount; $k++ )
			{
				// Get the edges associated with the face.
				//
				$face          = $faceList[ $k ];
				$faceEdgeList  = getEdgeList( $face );
				$faceEdgeCount = size( $faceEdgeList );

				clear( $indexList );

				// Check to see which shard each edge is in by determining which faces
				// share the edge.  Tally the number of times this edge is used for each
				// shard.
				//
				for ( $ii = 0; $ii < $faceEdgeCount; $ii++ )
				{
					// Get the face list for the edge.
					//
					$edgeFaceList  = getFaceList( $faceEdgeList[$ii] );
					$edgeFaceCount = size( $edgeFaceList );

					// Tally the number of edges in each shard for this face.
					//
					for ( $jj = 0; $jj < $edgeFaceCount; $jj++ )
					{
						if ( $face != $edgeFaceList[$jj] )
						{
							int $index = getIndex( $edgeFaceList[ $jj ] );
	
							$shardIndex = $shardIndexList[ $index ] - 1;
							$indexList[ $shardIndex ] = $indexList[ $shardIndex ] + 1;
						}
					}
				}

				$faceIndex = getIndex( $face );

				// Determine which shard this face "belongs" to.  This is done
				// by choosing the shard that this face shares the most edges with.
				//
				int $newIndex = $shardIndexList[ $faceIndex ];
				int $count    = -1;

				for ( $ii = 0; $ii < $shardCount; $ii++ )
				{
					if ( $count < $indexList[$ii] )
					{
						$newIndex = $ii + 1;
						$count    = $indexList[$ii];
					}
				}

				// If the new shard index has changed then reassign the shard to the 
				// new index.
				//
				if ( $newIndex != $shardIndexList[ $faceIndex ]) 
				{
//					print( "// Reassigning face " + $face + " from shard " ); 
//					print( $shardIndexList[ $faceIndex ] + " to shard " + $newIndex + "\n" );
		
					$shardIndexList[ $faceIndex ] = $newIndex;
				}
			}
		}
	}
}


proc string[] addFacesToShard( string $faceList[], int $shardIndex, int $shardIndexList[] )
//
// This procedure takes the given face list and adds the faces to the
// shard indicated by the shardIndex.  
//
// A unique (no duplicate faces) face list is returned.
//
{
	string $buffer[];
	string $newFaceList[];

	clear( $newFaceList );

	int $faceCount = size( $faceList );
	int $index     = 0;

	// For each face in the given list, 
	//
	//	- tokenize the faces so they are individual faces.
	//	- assign each face the given shard index.
	//
	for ( $i = 0; $i < $faceCount; $i++ )
	{
		//  Get a list of individual faces.
		//
		string $face = $faceList[ $i ];

//		tokenize( $face, "/[/]", $buffer );
		tokenize( $face, "[]", $buffer );
		string $baseName = $buffer[0];
//		tokenize( $buffer[1], "/:", $buffer );
		tokenize( $buffer[1], ":", $buffer );

		int $bufferSize = size( $buffer );

		int $start = (int) $buffer[0];
		int $end   = $start;

		if ( $bufferSize == 2 )
		{
			$end = (int) $buffer[1];
		}

		// Assign each face the shard index.  This loop will NOT
		// add the same face to the newFaceList twice.
		//
		for ( $j = $start; $j <= $end; $j++ )
		{
			if ( $shardIndexList[ $j ] == 0 )
			{
				$newFaceList[ $index ] = $baseName + "[" + $j + "]";
				$shardIndexList[$j]    = $shardIndex;
				$index++;
			}
		}
	}

	return $newFaceList;
}


proc int createShard( string $face, int $shardIndexList[], int $shardIndex, 
						     int $desiredFaceCount)
//
//  This procedure creates a single shard given the face to start the shard from and 
//  the desired number of faces to include in the shard.  This procedure returns the
//  number of faces actually allocated for the shard.
//
{
//	string $vertices;
//	string $faces;
	string $vertexList[];
	string $faceList[];
	string $uniqueFaceList[];
	string $previousFaceList[];
	int    $faceCount;
	int    $addFaceCount;
	int    $totalFaceCount = 0;

	int $index;

	int $eventCount = 0;
	int $maxTry     = 0;

	// For the given face, create a "shard" about the face by traversing the
	// vertex list to determine neighboring faces.
	//
	while ( $totalFaceCount < $desiredFaceCount )
    {
		// Convert the given face to vertices.
		//
		$vertexList = `polyListComponentConversion -toVertex $face`;
//		$vertices = `polyListComponentConversion -toVertex $face`;
//		tokenize( $vertices, " ", $vertexList ); 

		// With the vertex list, get a list of all faces which contain
		// these vertices.  Tokenize the list into individual faces.
		//
		$faceList = `polyListComponentConversion -toFace $vertexList`;
//		$faces = `polyListComponentConversion -toFace $vertexList`;
//		tokenize( $faces, " ", $faceList ); 
		$faceCount = size( $faceList );

		// Add the face list to the shard.  This procedure returns a
		// unique list of faces which where added to the shard.
		//
		$uniqueFaceList = addFacesToShard( $faceList, $shardIndex, $shardIndexList );
		$addedFaceCount = size( $uniqueFaceList );

		// If no faces where added from the last face list then try and find a
		// new face.
		//
		if ( $addedFaceCount == 0 ) 
		{
			int $count = size( $previousFaceList );

			if ( ( $eventCount < $maxTry ) && ( $count > 1 ) )
			{
				$face = $previousFaceList[ $eventCount ];
				$eventCount++;
				continue;
			}
			else 
			{
				break;
			}
		}

		// Add the new faces to the total face count and pick
		// a new face to iterate on.
		//
		else
		{
			$eventCount = 0;

			$totalFaceCount += $addedFaceCount;

			$index = rand( 0, $addedFaceCount );

			// Choose a new face to iterate on by randomly choosing a face from the
			// added faces list.  If we choose the previous face then get another.
			//
			while ( $face == $uniqueFaceList[ $index ] )
			{
				// If the added face count is one or less then we have exhusted the
				// face list to choose from.
				//
				if ( $addedFaceCount > 1 )
				{
					$index = rand( 0.0, $addedFaceCount );
				}
				else
				{
					break;
				}
			}
	
			// Store the new face to interate on.  
			//
			$face = $uniqueFaceList[ $index ];

			// If the face count added to the shard is greater then 1, then
			// store the list of faces added.  We do this so we have a larger 
			// list to choose from in case we fail to add shards in the future.
			//
			if ( $addedFaceCount > 1 )
			{
				clear( $previousFaceList );
				$previousFaceList = $uniqueFaceList;
				$maxTry = $addedFaceCount;
			}
		}
    }

	return $totalFaceCount;
}


proc edgePerturbation( string $object, int $shardCount, int $shardIndexList[], 
					   float $perturb, string $shardEdgeList[], int $verbose )
//
// This procedure applies a perturbation along the boundaries of each shard.  This
// is done to give the edges of the shards a bit more randomness.  The perturbation 
// is done by applying a random delta (no greater than +/- $perturb) to the edge
// vertices along a tangent to the surface normal.
//
{
	int    $index;
	int    $vertexCount[] = `polyEvaluate -vertex $object`;
	int    $perturbedVertices[];
	int    $faceCount = 0;
	int    $edgeCount;
	int    $pCount = 0;
	string $perturbOperations[];
	string $faceList[];
	string $vertexList[];


	for ( $i = 0; $i < $vertexCount[0]; $i++ )
	{
		$perturbedVertices[ $i ] = 0;
	}

	if ( $verbose == true )
	{
		print( "// Perturbing shard edges.\n" );
	}

	// For each shard, process its edge list and perturb the vertices of the edges.
	//
	for ( $i = 0; $i < $shardCount; $i++ )
	{
		string $edgeList[];

		// Expand the edge list into individual edges.
		//
		tokenize( $shardEdgeList[$i], " ", $edgeList );
		$edgeList  = expandList( $edgeList );
		$edgeCount = size( $shardEdgeList );

		// For each edge in the edge list, determine if the edge is not on
		// the extents of the surface (on the edge of the surface).  If it
		// is not then perturb its vertices.
		//
		for ( $j = 0; $j < $edgeCount; $j++ )
		{
			$vertexList     = getVertexList( $edgeList[ $j ] );
			$vertexCount[0] = size( $vertexList );

			// Perturb each vertex along the edge.
			//
			for ( $k = 0; $k < $vertexCount[0]; $k++ )
			{
				// Get the edge associated with this vertex.
				//
				int $process = true;

				$index = getIndex( $vertexList[$k] );

				// Do not perturn the same vertex twice.
				//
				if ( $perturbedVertices[ $index ] == 1 )
				{
					continue;
				}

				string $eList[] = getEdgeList( $vertexList[$k] );
				int    $count   = size( $eList );

				// If a vertex is associated with a face that does not share an edge
				// then do not perturb the edge vertex.  We don't want to perturb the
				// vertices on the edges of the surface so we do not extend the
				// vertices beyond the original surface.
				//
				for ( $ii = 0; $ii < $count; $ii++ )
				{
					$faceList  = getFaceList( $eList[$ii] );
					$faceCount = size( $faceList );

					// This edge is associated with only one face.
					// Do not perturb the vertex.
					//
					if ( $faceCount == 1 )
					{
						$process = false;
						break;
					}
				}

				// Perturb the vertex by determine the tangent to the surface
				// at the vertex (we just pick an edge for this) and randomly
				// rotate this tangent about the normal of the surface.  Then
				// apply the perturbation factor along the rotated vector.
				//
				if ( $process == true )
				{
					int $edgeIndex = rand( 0, $count );
					string $vList[] = getVertexList( $eList[ $edgeIndex ] );
	
					float $v1[] = `xform -q -t $vList[0]`;
					float $v2[] = `xform -q -t $vList[1]`;
	
					vector $tangent = unit( << $v2[0]-$v1[0], $v2[1]-$v1[1], $v2[2]-$v1[2] >> );
	
					float $x = rand( -$perturb, 0.0 ) * $tangent.x;
					float $y = rand( -$perturb, 0.0 ) * $tangent.y;
					float $z = rand( -$perturb, 0.0 ) * $tangent.z;
	
					string $op = "move -r -ls " + $x + " " + $y + " " + $z + " " + $vertexList[$k];
					$perturbOperations[ $pCount ] = $op;
					$pCount++;
	
					// Keep a list of perturbed vertices because we do not 
					// want (or need) to perturb the same vertex twice.
					//
					$perturbedVertices[ $index ] = 1;
				}
			}
		}

		// Do the perturbation after the offsets have been determined.
		//
		for ( $i = 0; $i < $pCount; $i++ )
		{
			eval( $perturbOperations[ $i ] );
		}
	}
}


proc string[] getShardEdges( string $object, int $shardCount, int $shardIndexList[], 
									int $verbose )
{
	string $edge;
	string $edges[];
	string $edgeList[];
	string $faceList[];
	int    $index0, $index1;
	int    $faceIndex;
    int    $edgeCount[] = `polyEvaluate -edge $object`;


	if ( $verbose == true )
	{
		print( "// Creating shard edges.\n" );
	}

	// Create a list of the edges which create a shard. 
	//
	for ( $i = 0; $i < $edgeCount[0]; $i++ )
	{
		$edge = $object + ".e[" + $i + "]";

		// Get the face list for the given edge.
		//
		$faceList  = getFaceList( $edge );

		// If there are more there are two faces associated with the
		// edge then check to see if it is a shard boundary.
		//
		if ( size( $faceList ) == 2 )
		{
			$faceIndex = getIndex( $faceList[0] );
			$index0    = $shardIndexList[ $faceIndex ] - 1;
			$faceIndex = getIndex( $faceList[1] );
			$index1    = $shardIndexList[ $faceIndex ] - 1;

			// If the shard indices are not the same then we have found
			// an edge between shard.  Store the edge in each shards edge
			// list.
			//
			if ( $index0 != $index1 )
			{
				$edges[ $index0 ] =	$edges[ $index0 ] + $edge + " ";
				$edges[ $index1 ] = $edges[ $index1 ] + $edge + " ";
			}
		}
	}

	return $edges;
}


proc int processLeftOverFaces( string $object, int $shardIndexList[], int $verbose )
//
// This procedure goes throught the shard index list and assign facets which are
// adjacent to the shard (and not in another shard) to the shard.
// Note that this method will probably have to be call many times to assign the
// remaining shards to the shard list.
//
// Note: this procedure is slow!!!!!!
//
//
{
	string $face;
//	string $faces;
//	string $edges;
	string $edgeList[];
	string $faceList[];
	int    $offset       = 100000;
	int    $polygonCount = 0;
	int    $shardIndex;
	int    $assignmentList[];
	int    $faceCount = size( $shardIndexList );


	int $totalCount = 0;

	for ( $i = 0; $i < $faceCount; $i++ )
	{
		// If the face as not been assigned to a shard then do it now.
		//
		if ( $shardIndexList[ $i ] == 0 )
		{
			$face = $object + ".f[" + $i + "]";

			// Get the edge list for the face.
			// With the edge list, get a list of all faces which contain
			// these edges.  Tokenize the list into individual faces.
			//
			$edgeList = `polyListComponentConversion -toEdge $face`;
//			$edges = `polyListComponentConversion -toEdge $face`;
//			tokenize( $edges, " ", $edgeList );
			$faceList = `polyListComponentConversion -toFace $edgeList`;
//			$faces = `polyListComponentConversion -toFace $edgeList`;
//			tokenize( $faces, " ", $faceList );
			$faceList = expandList( $faceList );

			int $edgeFaceCount = size( $faceList );
			int $faceIndex;

			// Determine if the faces around the given face are associated 
			// with a shard.  If so, assign this face to the shard.
			//
			// Note: we add an offset to the shard index when assigning it 
			//       to the new face.  This is so we do not propogate the shard 
			//       index to faces which have just be assigned a new shard.
			//       If this is not done first shard tends to get all the remaining
			//       facets.
			//
			for ( $j = 0; $j < $edgeFaceCount; $j++ )
			{
				// No need to check the we are trying to add to a shard.
				//
				if ( $face != $faceList[$j] )
				{
					$faceIndex  = getIndex( $faceList[ $j ] );
					$shardIndex = $shardIndexList[ $faceIndex ];

					if ( ($shardIndex != 0) && ($shardIndex < $offset) )
					{
						$shardIndexList[ $i ] = $shardIndex + $offset;

						$assignmentList[$polygonCount] = $i;
						$polygonCount++;
					}
				}
			}

			$totalCount++;
		}
	}

	// Go though the list and remove the offset we added to 
	// the shard index.
	//
	for ( $i = 0; $i < $polygonCount; $i++ )
	{
		$shardIndex = $shardIndexList[ $assignmentList[ $i ] ];

		if ( $shardIndex > $offset )
		{
			$shardIndexList[ $assignmentList[ $i ] ] -= $offset;
		}
	}

	if ( $verbose == true )
	{
		print( "// Processed: " + $polygonCount + " polygons out of " + $totalCount + ".\n" );
	}

	return $polygonCount;
}


proc fillShardsWithRemainingFaces( string $object, int $shardIndexList[], int $verbose )
//
// Given the initial shard list, place place the facets which where not assigned
// to a shard in the first pass into the shards.  If a facets in the shard list 
// has an index of 0 then it has not yet been assigned to a shard.  Iterate thourgh
// the shard list until all of the facets have been assign to an existing shard.
//
{
	int $polygonCount = 1;

	if ( $verbose )
	{
		print( "// Processing unallocate facets.\n" );
	}

	// While the number of polygons processed is greater then 0 then keep 
	// processing the face list.
	//
	while ( $polygonCount > 0 )
	{
		// Divy up the left over faces into the given shards.
		//
		$polygonCount = processLeftOverFaces( $object, $shardIndexList, $verbose );

		// We are done.
		//
		if ( $polygonCount == 0 )
		{
			break;
		}
    }
}


proc int[] determineShardIndices( string $object, int $shardCount, int $exactCount,
								  int $smoothing, float $edgePerturb, string $postOp, 
								  int $verbose )
//
// This procedure creates the given number of shards from the surfaces.
//
{
	int $shardIndexList[];
	int $shardIndex = 1;

    // The extacting command needs a set of faces to act on.
    // Since we want it to act on all of the faces, we use the
    // polyEvaluate command to find out how many there are.
    //
    int $faceCount[] = `polyEvaluate -face $object`;

	// Determine the rough number of faces per shard.
	//
	int $facesPerShard = $faceCount[0]/$shardCount;

	if ( $verbose == true )
	{
		print( "// FaceCount: " + $faceCount[0] + "\n" );
	}

	// Clear the shard index list.  This list will contain the shard index
	// of which each face belongs.
	//
	for ( $i = 0; $i < $faceCount[0]; $i++ )
	{
		$shardIndexList[$i] = 0;
	}

	// Create the number of initial shards.  These shards will
	// have roughly the desired number of faces in each shard.  However,
	// creating these shards will probably not use all the faces.
	//
	for ( $i = 0; $i < $shardCount; $i++ )
	{
		// Pick a random face to create a shard from.  Make sure the face is 
		// not alreading in a shard.
		//
		int $faceId = rand( 0, $faceCount[0] );

		int $count = 0;
		while ( $shardIndexList[ $faceId ] > 0 )
		{
			$faceId = rand( 0, $faceCount[0] );
			$count++;
			if ( $count > 5 )
			{
				break;
			}
		}

		string $face = $object + ".f[" + $faceId + "]";

		if ( $verbose == true )
		{
			print( "// Creating shard " + $shardIndex + " from face: " +  $face + "\n" );
		}

		// Create a shard.
		//
		int $actualCount = createShard( $face, $shardIndexList, $shardIndex, $facesPerShard );

		if ( $verbose == true )
		{
			print( "// Actual face count in shard: " +  $actualCount + "\n" );
		}

		$shardIndex++;
	}

	// If the exact shard count flag was thrown then make sure the remaining
	// polygons are placed into the shards which have already been created.
	//
	if ( $exactCount == true )
	{
		fillShardsWithRemainingFaces( $object, $shardIndexList, $verbose );
	}

	string $edgeList[];

	// Only create the edge list if we need it.
	//
	if ( ($smoothing == true) || ($edgePerturb > 0.0) )
	{
		$edgeList = getShardEdges( $object, $shardCount, $shardIndexList, $verbose );
	}

	// Smooth the shards if so desired.  The smoothing of a shard will check
	// each polygon and make sure it is in the shard that it has the most 
	// edges in common with.
	//
	if ( $smoothing == true )
	{
		smoothShardEdges( $object, $shardCount, $shardIndexList, $edgeList, $verbose );
	}

	// If the edge perturbation value is greater then 0.0, apply a perturbation to
	// each edge between shards.  This gives a bit more random look to the shard edges.
	//
	if ($edgePerturb > 0.0 )
	{
		edgePerturbation( $object, $shardCount, $shardIndexList, $edgePerturb, $edgeList, $verbose);
	}

	return $shardIndexList;
}



proc string surfaceShatterObject( string $object, string $shatterName, int $shardCount, 
								  int $exactCount, int $triangulate, int $smoothing, 
								  float $extrude, float $edgePerturb, int $original,
								  string $postOp, int $makeRigid, int $verbose )
//
// This procedure actually does the shattering of the given
// object.  We can assume that the object is either of
// nurbsSurface shape or a polygonal mesh shape, because the
// procedure that calls it will only do so for those types
// of objects.
//
{
	int    $faceCount[];
	int    $shardIndices[];
	string $shardList[];
    string $newObject   = "";
	string $shapeList[] = `ls -type shape`;

    // I want to know that parent transform of this
    // shape, so that I can use its name when I name
    // my new objects.
    //
    string $parent[] = `listRelatives -parent $object`;

    // First, get a polygonal version of this shape.
    //
    string $type = `nodeType $object`;

    if( $type == "nurbsSurface" )
    {
		// Check for history:  does listHistory return anything 
		// other than the node itself and its 'make' node?
		//
		string $hist[] = `listHistory $object`;
		if (size($hist) > 2)
		{	
			string $errString = "If you want to shatter " + $object + ", please first do Edit | Delete By Type -> History.";
			print($errString+"\n");
			return $newObject;
		}

        // If this shape is a nurbsSurface shape, then I
        // need to convert it into a polygonal mesh.
        //
        nurbsToPoly -ch 0 -format 1 -pc 400 $object;
        $newObject = getSelectedObject( 0 );
    	$faceCount = `polyEvaluate -face $newObject`;
    }
    else
    {
		// Check for history:
		// does listHistory return anything other than
		// the node itself?
		//
		string $hist[] = `listHistory $object`;
		if (size($hist) > 1)
		{	
			string $errString = "If you want to shatter " + $object + ", please first do Edit | Delete By Type -> History.";
			print($errString+"\n");
			return $newObject;
		}

        // If this shape is a polgonal mesh shape, then
        // I just duplicate it to get a version that I
        // will shatter, since I do not want to change
        // the original object.
        //
    	$faceCount = `polyEvaluate -face $object`;
        string $duplicate[] = `duplicate $object`;
        select $duplicate[0];

	  // If this object/hierarchy contained a rigid body or bodies, 
	  // the rigid body nodes have also bee duplicated. We do not want those
	  // copies.  Delete them now.  The ls command used here returns all
	  // rigidBody nodes at or below this level in the hierarchy.
	  //
	  delete `ls -objectsOnly -dag -allPaths -type rigidBody $duplicate[0]`;
    }

	// If we have a valid name then rename the new object.
	//
	if ( size( $shatterName ) > 0 )
	{
    	rename ( $shatterName );
	}
	else
	{
    	rename ( "shatter_" + $parent[0]);
	}

    $newObject = getSelectedObject( 0 );

	int $extractAllFaces = false;

    // Now, make sure that the polygonal mesh is made up of
    // only triangles.
    //
	if ( $triangulate == true )
	{
    	$faceCount = `polyEvaluate -face $newObject`;
    	polyTriangulate -ch 0 ($newObject+".f[0:"+($faceCount[0]-1)+"]");
    	$faceCount = `polyEvaluate -face $newObject`;
	}

	// If the number of shards in greater then the number
	// of faces in the object then extract all the faces.
	//
	if ( $faceCount[0] <= $shardCount )
	{
		$extractAllFaces = true;
	}

	// If we are extracting all the polygons then there is no need to
	// create a shard index list.  Just do the poly chip off and
	// separate the surface.
	//
	if ( $extractAllFaces )
	{
		int    $maxFace = $faceCount[0] - 1;

		for ( $i = $maxFace; $i >= 0; $i-- )
		{
			string $face = $newObject + ".f[" + $i + "]";
			polyChipOff -kft true -duplicate true $face;
		}

		// Separate the shards and indicate that we do not want
		// to keep the original object (false).
		//
		$shardList = separateShards( $newObject, false, $verbose );
	}
	else
	{
		$shardIndices = determineShardIndices( $newObject, $shardCount, $exactCount, 
											   $smoothing, $edgePerturb, $postOp, $verbose);

		// Separate the shards into individual shapes.
		//
		if ( strcmp( $postOp, "sets") )
		{
			extractShards( $newObject, $shardIndices, $verbose );
	
			// Separate the shards and indicate that we do not want
			// to keep the original object (false).
			//
			$shardList = separateShards( $newObject, false, $verbose );
		}
	}

    // I do not want the construction history to be kept around,
    // so I delete it here, BEFORE I do the soft body work.
    //
    delete -ch $newObject;

	// Set the display status for the original object.
    //
    processOriginalObject( $object, $newObject, $original, $postOp, $makeRigid );

 	int $makeConnections = false;

    if ( $original == 4 )
    {
    	$makeConnections = true;
    }

	postProcessShards( $newObject, $shardList, $shardIndices, $extrude, $triangulate, 
					   $postOp, $shapeList, $object, $makeConnections, $verbose );

	// Determine what to do with the original object.
	//
//	objectDisplay( $object, $original );

	if (size($newObject)>0)
		print("// Shatter succeeded\n");

    return $newObject;
}


proc string[] _surfaceShatter ( string $name, int $shradCount, int $exactCount, int $smoothing, 
								int $triangulate, float $extrude, float $perturb, int $original,
								string $postOperation, int $makeRigid, int $verbose )
//
// This procedure looks through the selection list and calls
// surfaceShatterObject() on only the nurbsSurface or
// polygonal mesh shapes.  
//
{
    // The return value.
    //
    string $result[];
    clear( $result );

    // This array is used to avoid shattering the same object twice
    // during the running of this script.  This might happen if both
    // the transform and the shape of an object are selected.
    //
    string $objectsDone[];
    clear( $objectsDone );

    string $selectedShapes[] = getSelectedList( "allGeometry" );

    int $i;
    for( $i = 0; $i < size( $selectedShapes ); $i ++ )
    {
        // If this shape has already been shattered, then skip over
        // it.
        //
        if( findInStringArray( $selectedShapes[$i], $objectsDone ) == -1 )
        {
            //
            // If this shape is not nurbsSurface or polygonal mesh shape,
            // then display a warning and continue.
            //
            string $type = `nodeType $selectedShapes[$i]`;

            if( ( $type == "nurbsSurface" ) || ( $type == "mesh" ) )
            {
				string $newObject;

                // If for some reason the surfaceShatterObject() procedure
                // returns a blank string, then display a warning and
                // continue.  This could have returned an error, but I
                // decided that I do not want to exit the script for this
                // reason.
                //
                $newObject = surfaceShatterObject( $selectedShapes[$i], $name, $shradCount, 
												   $exactCount, $triangulate, $smoothing, 
												   $extrude, $perturb, $original,
												   $postOperation, $makeRigid, $verbose );

                if( size( $newObject ) > 0 )
                {
                    $result = appendSingleToStringArray( $result, $newObject );
                }
                else
                {
                	warning("Could not shatter " + $selectedShapes[$i] + ".");
                }
            }
            else
            {
                warning( $selectedShapes[$i] + " can not be shattered.");
            }

            //
            // Note that this shape has been shattered so that we do not do it again
            // during this execution of the script.
            //
            $objectsDone = appendSingleToStringArray( $objectsDone, $selectedShapes[$i] );
        }
    }

    if ( $original == 4 )
    {
        string $parent[] = `listRelatives -parent $selectedShapes`;
        select $parent;
    }
    else
    {
        select $result;
    }

    return $result;
}


global proc string[] surfaceShatter( string $name, int $shradCount, int $exactCount, 
									 int $smoothing, int $triangulate, float $extrude, 
									 float $perturb, int $seedValue, int $original,
									 string $postOperation, int $makeRigid, int $verbose )
//
// This is the global procedure that the user actually calls.  It manages the
// wiat cursor and traps any errors or failures that might happen during the
// execution of this script.
//
{
	string $result[];
    clear( $result );

	if( `licenseCheck -type complete` == 0 )
	{
		warning("You are not licensed to use the Shatter Effect.");
		return $result;
	}

    // Store the currently slected objects so that we can
    // restore them if we detect some error.
    //
    string $lastSelectionList[] = `ls -sl`;

	float $time = `timerX`;

    waitCursor -state on;

	// If the seed value is being supplied then use it.
	//
	if ( $seedValue != 0 )
	{
		seed( $seedValue );
	}

    if ( catch( $result = _surfaceShatter( $name, $shradCount, $exactCount, $smoothing, 
										   $triangulate, $extrude, $perturb, $original,
										   $postOperation, $makeRigid, $verbose) ) )
    {
        // If an error is detected, call our error-handling
        // procedure, restore the selection list, and clear
        // whatever might be in the result array.  We do not
        // exit the script here, because we want the waitCursor
        // command to turn off the wait cursor.
        //
        shatterErrorHandler( "Surface Shatter" );
        select $lastSelectionList;
        clear( $result );
    }

    waitCursor -state off;

	if ( $verbose )
	{
		$time = `timerX -st $time`;
		print( "// Elapsed Time: " + $time + "\n" );
	}

    return $result;
}


