I seem to have found a cheap way of fixing the “cup problem”
if you scale the cup wide enough, so the distance from one particle to the other on the other end is far enough you dont get that big of a problem
You get minor random cut extrusion.
I seem to have found a cheap way of fixing the “cup problem”
if you scale the cup wide enough, so the distance from one particle to the other on the other end is far enough you dont get that big of a problem
You get minor random cut extrusion.
AtrusDni
this voronoi shatter is besides being a bit slow (which is no offense) extremely sweet!
and such a great idea to get around the maya rigid boody dynamics problems.
if i would get some time (which is quite unlikely in the upcoming months) i would love to incorporate it with my already existent shatter script from the start post(which had the biggest problem while using the maya shatter:))
keep up 
alex
Hi all, I’ve been watching this thread with great interest. Many thanks to AtrusDni for releasing your script. I hope I can add something to the cause… I’ve attached a simple scene, with a cube, a cutPlane and a few locators. I’ve created an expression to control the position and rotation of the cutplane without using constraints:
float $aX = firstPoint.translateX;
float $aY = firstPoint.translateY;
float $aZ = firstPoint.translateZ;
float $bX = secondPoint.translateX;
float $bY = secondPoint.translateY;
float $bZ = secondPoint.translateZ;
float $mPX = ($aX+$bX)/2;
float $mPY = ($aY+$bY)/2;
float $mPZ = ($aZ+$bZ)/2;
midPoint.translateX =$mPX;
midPoint.translateY =$mPY;
midPoint.translateZ =$mPZ;
// Convert to vectors
vector $midPoint =<<$mPX, $mPY, $mPZ>>;
vector $aPosVector = << $aX, $aY, $aZ >>;
vector $bPosVector = << $bX, $bY, $bZ >>;
// Calculate direction vector
vector $dir = $aPosVector - $bPosVector;
// Normalize
$dir = $dir / mag($dir);
vector $y = <<1,0,0>>;
vector $x = <<0,1,0>>;
float $dotY = dotProduct($y, $dir , 0);
float $angY = 0-((rad_to_deg(acos($dotY))) -90);
float $dotX = dotProduct($x, $dir , 0);
float $angX = (rad_to_deg(acos($dotX)))-90;
//print ("
X rotation ="+$angX +"
Y rotation ="+$angY+"
");
midPoint.rotateZ =0;
midPoint.rotateX =$angX;
midPoint.rotateY =$angY;
//apply values to cutplane
polyCut1.cutPlaneCenterX= $mPX;
polyCut1.cutPlaneCenterY= $mPY;
polyCut1.cutPlaneCenterZ= $mPZ;
polyCut1.cutPlaneRotateX =$angX;
polyCut1.cutPlaneRotateY =$angY;
It should be pretty easy to chop this into your script if you wish to get rid the aim constraints…
Somebody more expert than me should maybe take a look at the scene and check I figured the rotation correctly, I’m at my maths limit:)
Dave.
Hi again, I’d also like to share a tip about particle positioning. I really like using particles to visualise the result, but there are problems if they are not contained within the object.
What worked for me was to place an emitter within the object, flip the objects normals, emit the particles and make collide with the object. Stop emitting when you have enough particles, but you could let them rattle around inside for a while… then don’t forget to flip back the normals.
Dave.
@ Dave -
Nice!!! I will have to take a look at your code a bit closer. By the way, you wouldn’t know how to compare the angle between 2 vectors would you?
One more problem I need to know is to tell if a point lies in front of, or behind a plane . . . 
Thanks for your help though, its much appreciated.
Hi, to compare two angles is more or less how the expression works. Its these mel commands:
float $dot = dotProduct($vector1, $vector2, 1); // 1 means normalise 0 not normalised
and the angle in radians :
float $ang = acos($dot); // arccosine of the dot product
and to convert the result to degrees:
rad_to_deg($ang);
Are you going to use this to detect concave objects, ie the ‘cup’ problem? I felt that was more to do with polyCloseBorder not making a good choice…
Dave
Hi again, I have an idea to drastically speed up the process on large splits. If you divide your object into ‘voxels’ , and then create a point in random space within that voxel, you will automatically gain a distance tolerance. eg if your are sampling from one point you don’t need to measure more than two voxels away, so max calculations for any point is 125 (2 voxels in all directions creates a 5x5x5 space). It’s not an original idea, i’m sure other people are using it, and definitely in 2d:
http://home.scarlet.be/zoetrope/voronoi/index.htm
and probably 3d:
http://morfogen.blogspot.com/2007/07/3d-voronoi-patio.html
Dave.
I had an idea for particle placement. Emit from the surface of the object with a noise texture, and possibly a negative emission speed (to shoot the particle nagetive the normals)
OR mor complicated
reverse the nomals
emit particles off surface, rand speed and have them collide with the surface.
reset normals and shatter. (if needed)
@ davegreenwood -
Actually no, I was going to use the angle between the vectors to remove unwanted locators. Basically locators outside of the mesh. I could have inverted the normals and emitted inside the mesh, thats a good workaround, but im looking for a cleaner approach.
Yes the issue with the cup is that the poly close boarders command does not understand how to close the border on a mesh with more then 1 poly shell.
Soooo my workaround that i was working on last night, is to evaluate the number of shells the mesh has after the poly cut. If there is more then one, then this would be an “invalid” cut and it should not use that cut angle. Still working out the kinks at the moment. If anyone wants to jump it and give it a shot, please be my guest. I would appreciate any outside help / ideas that people have.
And thanks again davegreenwood, I appreciate your help. Me + Math = Error. Im good at implimenting ideas, just not the math side of things 
P.S. - That is a good idea about the voxels. I do like the randomness and weird shapes that are made from the particles, especially from a point emitter, but maybe random shatter could use the voxel approach and yes that will speed it up a lot. Hmmmm gears turning . . . .
Hey guys cool stuff.
I am new to maya and cannot seem to get AtrusDni’s script to do anything. Should the script work if I simply execute it from the script editor?
Hi’yall! AtrusDni, I had already hacked the math Dave Greenwood mentioned into the script, to see if we’d get a big speed boost by using a matrix transformation to aim the cubes instaed of locators and aimConstraints. Thought I’d share-
// Voronoi Shatter with Matrix Aim
proc callVoronoi()
{
// Show the dialog box:
string $crackOffsetImput;
string $result = `promptDialog
-title "Voronoi Shatter"
-message "Crack Offset:"
-button "OK" -button "Cancel"
-defaultButton "OK" -cancelButton "Cancel"
-dismissString "Cancel"`;
// Use the command again in query mode to
// get the text:
if ($result == "OK") {
$crackOffsetImput = `promptDialog -query -text`;
}
if ($result == "Cancel"){
$crackOffsetImput = 0.004;
}
// Variables
float $crackOffset =$crackOffsetImput;
// Gather selected items
string $s[] = `ls -sl -fl`;
// Store particle name and object name
$pTName = $s[0];
string $objName = $s[1];
// Store particle shape name and particle count
string $pRelatives[] = `listRelatives -s $s[0]`;
$pSName = $pRelatives[0];
int $pCount = `getAttr ($pSName+".count")`;
// Pass selection to the voronoi shatter procedure
voronoiShatter($pTName, $pSName, $pCount, $objName, $crackOffset);
}
callVoronoi();
// The main voronoi shatter procedure
proc voronoiShatter(string $pTName, string $pSName, int $pCount, string $objName, float $crackOffset)
{
int $amount = 0;
progressWindow
-title "Voronoi Shatter"
-progress 0
-status "Initializing . . ."
-isInterruptable true
-maxValue $pCount;
// Disable the undo que
undoInfo -state off;
// Create shard material if it doesn't exist
int $shardMatExists = `objExists voronoiShardMaterial`;
if($shardMatExists == 0)
{
string $mat = `shadingNode -asShader lambert -name voronoiShardMaterial`;
sets -renderable true -noSurfaceShader true -empty -name ($mat + "SG");
connectAttr -f ($mat + ".outColor") ($mat + "SG.surfaceShader");
setAttr ($mat + ".color") -type double3 1 1 0 ;
}
// Create an empty group node to store shards
string $shardsGRP = `group -empty -n "shardsGRP"`;
// Hide particles
setAttr ($pTName + ".visibility") 0;
// Create cut block
string $cutBlock = voronoiCreateCutBlock($objName);
// Convert all the particles to locators
//string $locGRP = voronoiParticlesToLocators($pSName);
//setAttr ($locGRP + ".visibility") 0;
// Get particle count
int $pCount = `getAttr ($pSName + ".count")`;
// Go thru every pt and compare it to every other pt
// Find the midpoint, create shatter cube, and shatter
for($a=0;$a<$pCount;$a++)
{
// Check if the dialog has been cancelled
if ( `progressWindow -query -isCancelled` ) break;
// Check if end condition has been reached
if ( `progressWindow -query -progress` >= $pCount ) break;
$amount += 1;
// Store worldspace translate of a
float $aPos[] = `getParticleAttr -at worldPosition ($pSName + ".pt[" + $a + "]")`;
// Variable to store active shard mesh name
string $activeShard = "";
// Duplicate mesh to shatter
string $dupMesh[] = `duplicate -rr $objName`;
setAttr ($objName + ".visibility") 0;
$activeShard = $dupMesh[0];
setAttr ($activeShard + ".visibility") 1;
// Go thru each locator
for($b=0;$b<$pCount;$b++)
{
// Check that its not going to itself
if($a != $b)
{
// Store worldspace translate of b
float $bPos[] = `getParticleAttr -at worldPosition ($pSName + ".pt[" + $b + "]")`;
// Find the midpoint between locator A and locator B
vector $midPoint = <<(($aPos[0] + $bPos[0])/2), (($aPos[1] + $bPos[1])/2), (($aPos[2] + $bPos[2])/2)>>;
// Convert to vectors
vector $aPosVector = {$aPos[0],$aPos[1],$aPos[2]};
vector $bPosVector = {$bPos[0],$bPos[1],$bPos[2]};
// Calculate direction vector
vector $dir = $aPosVector - $midPoint;
// Normalize
$dir = $dir / mag($dir);
// Calculate new offset midpoint
vector $offMidPoint = $midPoint + ($crackOffset * $dir);
// Position cut block
string $cutCube[] = `duplicate -rr $cutBlock`;
setAttr ($cutCube[0] + ".visibility") 1;
setAttr ($cutCube[0] + ".translateX") ($offMidPoint.x);
setAttr ($cutCube[0] + ".translateY") ($offMidPoint.y);
setAttr ($cutCube[0] + ".translateZ") ($offMidPoint.z);
rotateObjectToVector($cutCube[0],$dir);
// Cleanup
// Do Cut procedure
vector $cutBlockRot = `xform -q -ws -rotation $cutCube[0]`;
int $numFace[] = `polyEvaluate -f $activeShard`;
polyCut -ch off -df 1 -pc ($offMidPoint.x) ($offMidPoint.y) ($offMidPoint.z) -ro ($cutBlockRot.x + 90) ($cutBlockRot.y) ($cutBlockRot.z) ($activeShard + ".f[0:" + $numFace[0] + "]");
// Evaluate the new cut faces
int $numCutFace[] = `polyEvaluate -f $activeShard`;
polyCloseBorder -ch 0 $activeShard;
// Evaluate the new cut and filled faces
int $numNewFace[] = `polyEvaluate -f $activeShard`;
// Go through all the new faces and assign new shader
for($f=($numCutFace[0]);$f<($numNewFace[0]);$f++)
{
// Assign the shard material to the new face
sets -e -forceElement voronoiShardMaterialSG ($activeShard + ".f[" + $f + "]");
}
select -cl;
// Cleanup
if(`objExists $cutCube[0]`)
{
delete $cutCube[0];
}
}
else
{
//delete $dupMesh[0];
}
}
// Parent shard under group node
parent $activeShard $shardsGRP;
// Refresh the viewport
//select -cl;
refresh();
progressWindow -edit
-progress $amount
-status ("Voronoi Shatter step " + $amount + " of " + $pCount + " completed . . .");
}
progressWindow -endProgress;
// Delete the cut block
delete $cutBlock;
undoInfo -state on;
}
global proc rotateObjectToVector(string $object,vector $V)
{
float $m[16] = `getAttr ($object+".worldMatrix")`; // the original matrix
float $xLen = `mag <<$m[00],$m[01],$m[02]>>`;
float $yLen = `mag <<$m[04],$m[05],$m[06]>>`;
float $zLen = `mag <<$m[08],$m[09],$m[10]>>`;
vector $A = unit(cross($V,<<rand(1),rand(1),rand(1)>>)); // dummy cross 1
vector $B = unit(cross($V,$A)); // dummy cross 2
vector $X = ($A * $xLen);
// -1 for reverse Y axis
vector $Y = -1 * ($V * $yLen);
vector $Z = ($B * $zLen);
xform -matrix
($X.x) ($X.y) ($X.z) 0
($Y.x) ($Y.y) ($Y.z) 0
($Z.x) ($Z.y) ($Z.z) 0
$m[12] $m[13] $m[14] 1
$object;
}
// Creates the cut block
proc string voronoiCreateCutBlock(string $objName)
{
// Find the bounding box of obj
float $bbox[] = `exactWorldBoundingBox $objName`;
float $biggestDimension = abs($bbox[3]-$bbox[0]);
if (abs($bbox[4]-$bbox[1]) > $biggestDimension)
{
$biggestDimension = abs($bbox[4]-$bbox[1]);
}
if (abs($bbox[5]-$bbox[2]) > $biggestDimension)
{
$biggestDimension = abs($bbox[5]-$bbox[2]);
}
// Now that we have the biggest dimension, lets increase it a bit
$biggestDimension = $biggestDimension * 4;
// Create the cut block geometry and store its name
string $cutBlockCreate[] = `polyPlane -w 1 -h 1 -sx 1 -sy 1 -ax 0 1 0 -cuv 2 -ch 0 -n "cutBlock"`;
string $cutBlockName = $cutBlockCreate[0];
// Set the scale of the plane
setAttr ($cutBlockName + ".scaleX") $biggestDimension;
setAttr ($cutBlockName + ".scaleZ") $biggestDimension;
// Extrude the plane into a block
polyExtrudeFacet -ch 0 -ltz ($biggestDimension * 2) -smoothingAngle 0 ($cutBlockName+".f[0]");
// Freeze transforms
makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 $cutBlockName;
// Hide the cut block
setAttr ($cutBlockName + ".visibility") 0;
select -cl;
return $cutBlockName;
}
From the looks of it we’re getting about a 5% speed increase, so the big time killer is the definitely the polycut command. Reducing the # of cuts will definitely help, but I think we need a slightly different approach to get more speed.
I was personally looking into a hybrid of your particle-based approach, and what Dave G posted a link to, with a eye on compiling it to C for speed. The way you find the distance between the points is a brute-force MEL way of calculating a 3d Voronoi texture.
Thinking of taking something like the math behind Qhull, and using it to generate the shattered geometry from a set of points within the original object, not unlike what this guy does here - http://home.scarlet.be/zoetrope/voronoi3d/index.htm If we can somehow find a way to trim the shattered pieces with the original shape in C without using booleans, polyCut or any pokey Maya commands, we should get something pretty zippy, but that’s where my math breaks down. Any ideas, anybody?
Malik!! Awesome! Now we got the aim working without constraints! Yes, I wasn’t expecting much of a speed boost, but the code will be much cleaner now.
Yes, the biggest improvement to speed will be isolating only the cuts that need to be performed from the ones that do not. Really tricky, but I think dave’s idea of voxels and isolating the distance of influence will be a big help.
Yes, doing a C compile would be super slick, but I couldn’t even begin to help with that. That would be the ideal approach though in terms of speed.
any chance this could get multi-threaded? I doubt it’s easy but seeing 1 of 16 threads active on this machine makes baby Nehalem cry.
hey
to make it work
first u need to create particles, then select the particles and then the cube or object and run the script
culprit for the cup problem is polyCloseBorder command, now we need to find a way to do the same thing but using other method/command/script.
Is there a way of testing how many shells an object has in mel? If so then you could test if there was more than one shell and if so select the border edges and bridge them. I think this script could really benefit from being converted to using pymel.
polyBridgeEdge requires equal number of border edges to be selected for source and target so how can you bridge edges if you have 2 or more shells with different number of border edges ?
I think this script could really benefit from being converted to using pymel.
speedwise ?!?
Odd, I was sure bridge edges was smart enough to terminate edges when the counts did not match.
And yes I think pymel could increase the speed and allow usage of some api commands that might be useful.
my implementation was first written in MEL and then translated in python, i haven`t notice any speed improvement.
P.S.
i`ve used python because i needed some API to do the cleaning part (remove locators outside the mesh)