View Full Version : Particle to particle collision = new particles?
razilon 02-07-2008, 03:34 PM I've set up 2 separate particle emitters and set each of them up as the other's goal so that the particles attract one another. Now I'd like to be able to detect when particleA hits particleB, kill particleA and particleB, and emit a new particleC (different particle object).
I know how to emit new particles when particles collide with a surface (via the particle collision event editor), but this doesn't allow particle to particle collision. I'm assuming there's a way to get what I want using per particle expressions, but I figured I'd see if anyone out there loves me enough to point me in the right direction before I grunt this out.
Thanks
|
|
ranxx
02-07-2008, 04:36 PM
that is an O(n2) operation, which will be extremely time consuming. That is, the time it will take will go up in the same way as multiplying the number of particles in emitter A times the number of particles in emitter B. I don't believe there is a way to do this natively in maya. If you consider doing this I would try to accomplish it with a plugin instead of expressions, as expressions are going to be very slow (depending upon your # of particles).
-ranxx
I've set up 2 separate particle emitters and set each of them up as the other's goal so that the particles attract one another. Now I'd like to be able to detect when particleA hits particleB, kill particleA and particleB, and emit a new particleC (different particle object).
I know how to emit new particles when particles collide with a surface (via the particle collision event editor), but this doesn't allow particle to particle collision. I'm assuming there's a way to get what I want using per particle expressions, but I figured I'd see if anyone out there loves me enough to point me in the right direction before I grunt this out.
Thanks
razilon
02-07-2008, 05:19 PM
Is there a way to list the objects currently being affected by a field?
I've got per particle radial fields on these things so that they don't interpenetrate. If I could (in a per particle expression) check whether there are any objects/particles within the grasp of the current particle's radial field, I could use that as a sort of collision detection.
ranxx
02-07-2008, 05:54 PM
that would be neat, but I don't think you can get that. I think you will have to test distances between particles, and if it goes below a threshold than you can treat it as a collision and emit your thrid particle.
-ranxx
Is there a way to list the objects currently being affected by a field?
I've got per particle radial fields on these things so that they don't interpenetrate. If I could (in a per particle expression) check whether there are any objects/particles within the grasp of the current particle's radial field, I could use that as a sort of collision detection.
razilon
02-07-2008, 07:18 PM
Here's the test scene I came up with. It works, but I can't help but believe there's a more efficient way to do this. Here's what I did:
1) created 3 particle objects (2 are set with each other as goals, so that they try to collide with each other, the third is just to hold new particles spawned by the collisions)
2) created lifespanPP expressions for the 2 colliding particle systems
int $numParts = `particle -q -ct particle2`;
float $partPos[] = `getParticleAttr -at position -array true particle2`;
vector $pos = particleShape1.position;
int $done = 0;
int $curPos = 0;
while ((!$done) && ($curPos < $numParts))
{
int $index = 3 * $curPos;
float $partDist = sqrt( `pow (($pos.x) - $partPos[$index]) 2` + `pow (($pos.y) - $partPos[($index + 1)]) 2` + `pow (($pos.z) - $partPos[($index + 2)]) 2`);
if ($partDist < 0.2)
{
// setParticleAttr -at "lifespanPP" -fv 0 ("particleShape1.pt[" + $curPos + "]");
particleShape1.lifespanPP = 0;
emit -o particle3 -pos ($pos.x) ($pos.y) ($pos.z);
$done = 1;
}
$curPos = $curPos + 1;
}
Some issues to fix or think about (tips are welcome):
1) Both particle systems run the distance tests against each other (calculate all distances twice). I tried to avoid that with the line commented out above, but it didn't work correctly (all particles from the other particle system disappear when it is executed).
2) Is it faster to query all particle positions at once (like I did above), or wrap the query into the inner loop (query 1 position at a time)?
rxgeez
02-07-2008, 07:54 PM
Is there a way to list the objects currently being affected by a field?
I've got per particle radial fields on these things so that they don't interpenetrate. If I could (in a per particle expression) check whether there are any objects/particles within the grasp of the current particle's radial field, I could use that as a sort of collision detection.
You can use the command inputForce[] on the particle. For example:
if (inputForce[0] > 0)
"lifespanPP = 0";
You have to know which force number you want. You have to know the order you connected the forces. If you added gravity then turbulence then radial and you want to find out if the radial is influencing the particle then it would be inputForce[2]. Dont know it this will help.
razilon
02-07-2008, 08:00 PM
Interesting...any idea how inputForce works with a per particle radial field? I suppose if inputForce only tells me that particle2's radial field is in effect, that could still help crop out some unnecessary calculations (e.g. if (inputForce[0] > 0)...do all the other calculations I wrote up).
ranxx
02-07-2008, 08:19 PM
hmmm, I wonder if you set a max distance for your force to your particle's "radius", then if input force would be 0 unless it was within that distance ? I know you wouldn't want to do this in your scene because you want the particles to attract each other, but maybe you could make a second per partical radial field with a small max distance and no attenuation, and test the inputforce[] for this field. That might be a way to get your collisions.
About your code .. I noticed you are calling sqrt() alot, you could get rid of this call by testing against the distance squared. Not sure about the other optimizations though. To me it would make more sense to do this not in a per particle expression, but in a runtime script which gets executed every substep. This way you wouldn't have to get all of the particle positions each frame multiple times (just pull in all particle1's positions, all particle2's positions, and do a nested for loop to compare them).
-ranxx
Interesting...any idea how inputForce works with a per particle radial field? I suppose if inputForce only tells me that particle2's radial field is in effect, that could still help crop out some unnecessary calculations (e.g. if (inputForce[0] > 0)...do all the other calculations I wrote up).
razilon
02-07-2008, 08:48 PM
Where would you set up the runtime script?
ranxx
02-07-2008, 09:37 PM
put a locator in your scene and make an expression which always evalutes on it's translateY attribute. I haven't tested this code with large numbers of particles, but in theory it should be faster (it might not be, because of some optimizations that maya does for particle expressions that I'm not aware of). The only way to be certain is to test it. I've pasted the code below:
int $num1Parts = `particle -q -ct particle1`;
float $part1Pos[] = `getParticleAttr -at position -array true particle1`;
int $num2Parts = `particle -q -ct particle2`;
float $part2Pos[] = `getParticleAttr -at position -array true particle2`;
int $ncollisions = 0;
int $i;
for ($i=0;$i<$num1Parts;$i++){
int $j;
float $cur_x1 = $part1Pos[$i*3];
float $cur_y1 = $part1Pos[$i*3+1];
float $cur_z1 = $part1Pos[$i*3+2];
for($j=0;$j<$num2Parts;$j++){
float $cur_x2 = $part2Pos[$j*3];
float $cur_y2 = $part2Pos[$j*3+1];
float $cur_z2 = $part2Pos[$j*3+2];
float $distance_squared = ($cur_x1-$cur_x2)*($cur_x1-$cur_x2)+($cur_y1-$cur_y2)*($cur_y1-$cur_y2)+($cur_z1-$cur_z2)*($cur_z1-$cur_z2);
if ($distance_squared < 0.4){
// collision:
print("collision at " + $cur_x1 + " : " + $cur_y1 + " : " + $cur_z1 + "\n");
$ncollisions++;
emit -o particle3 -pos $cur_x1 $cur_y1 $cur_z1;
particle -e -attribute lifespanPP -order $i -fv 0 particleShape1;
particle -e -attribute lifespanPP -order $j -fv 0 particleShape2;
}
}
}
locator1.translateY = $ncollisions;
-ranxx
Where would you set up the runtime script?
razilon
02-08-2008, 06:05 PM
Excellent...it is indeed faster. I made some small modifications to simplify usage and extensibility. I stuck most of the code into a function:
global proc int partCollisionGen(string $part1, string $part2, string $part3, float $distSq)
{
int $num1Parts = `particle -q -ct $part1`;
float $part1Pos[] = `getParticleAttr -at position -array true $part1`;
int $num2Parts = `particle -q -ct $part2`;
float $part2Pos[] = `getParticleAttr -at position -array true $part2`;
int $ncollisions = 0;
int $i;
for ($i = 0; $i < $num1Parts; $i++)
{
int $j;
float $cur_x1 = $part1Pos[$i * 3];
float $cur_y1 = $part1Pos[$i * 3 + 1];
float $cur_z1 = $part1Pos[$i * 3 + 2];
for ($j = 0; $j < $num2Parts; $j++)
{
float $cur_x2 = $part2Pos[$j * 3];
float $cur_y2 = $part2Pos[$j * 3 + 1];
float $cur_z2 = $part2Pos[$j * 3 + 2];
float $distance_squared = ($cur_x1 - $cur_x2) * ($cur_x1 - $cur_x2)
+ ($cur_y1 - $cur_y2) * ($cur_y1 - $cur_y2)
+ ($cur_z1 - $cur_z2) * ($cur_z1 - $cur_z2);
if ($distance_squared < $distSq)
{
$ncollisions++;
emit -o $part3 -pos $cur_x1 $cur_y1 $cur_z1;
particle -e -attribute lifespanPP -order $i -fv 0 $part1;
particle -e -attribute lifespanPP -order $j -fv 0 $part2;
}
}
}
return $ncollisions;
}
So now the expression on the locator is just:
locator1.translateY = partCollisionGen("particle1", "particle2", "particle3", 0.04);
Thanks for the learning experience guys.
CGTalk Moderation
02-08-2008, 06:05 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.
vBulletin v3.0.5, Copyright ©2000-2012, Jelsoft Enterprises Ltd.