PDA

View Full Version : Particles Swimming in a box


Smithjoe1
11-15-2006, 10:50 AM
Hey all

I'm wanting to make a bunch of particles to swim around inside of a box, but I'm unsure of exactally how to code it.

What I want to happen is to get a particles location and velocity, shoot a ray or something and detect if it intersects a polygon within a ceartin distance, and have it veer off at a random direction over the time of several frames. Perhaps several objects, though I'm unsure of what functions I could use to use work it out, but someone must. :)

Thanks in advance.

sunit
11-15-2006, 12:56 PM
you'll probably want to use particle runtime expressions for your sim. some of the seminal flocking/steering work was done by craig reynolds:

http://www.red3d.com/cwr/boids/

and many of the ideas port over to maya particles in a more or less straightforward way.

rather than trying to calculate mesh intersections on a per-particle basis, i would suggest using mesh primitives to approximate your boundaries. this way, you don't have to deal with complex meshes, just simple algorthmic geometry (you may have to use some trigonometry here).

you might also want to post in the dynamics board - you'll probably get a few more responses.

-sunit

Smithjoe1
11-15-2006, 11:13 PM
Thanks for the reply

I was wondering if the dynamics would have been more suitable, but I figured the coding would have found home here.

I was thinking bounding boxes or something to contain it, but a boundry wouldn't suffice I wouldnt think becasue once they get to the edge, they die, which isnt waht I want, rather to have them turn around a random angle between 110 and 150 degrees when they get within 2 units of a ceartin mesh.

The intersection is handled quite well with particle collisions, but its not what I need to happen, I need to detect a polygon following its direction and turn if its in proximity. A simple AI routine really, it'd be a bit bad to just have it hit the edge, bounce off there, wouldnt behave well at all.

Something like this,
get a particles velocity vector
find its direction
cast a ray for 2 units following the vector
detect if that ray colides with a polygon
find the owner of the polygon
compare it to an array of object names
decide weather to work or ignore it

then work out the time it will take until it intersects it
halve that time
rotate a random amount over that time
repeat.

sunit
11-16-2006, 12:46 AM
Thanks for the reply
Something like this,
get a particles velocity vector
find its direction
cast a ray for 2 units following the vector
detect if that ray colides with a polygon
find the owner of the polygon
compare it to an array of object names
decide weather to work or ignore it


that's the right idea. reynolds' method involves projecting a cylinder in front of your boid that acts as its 'sight' - in the simple case, you can do as well with just a line/vector projection in front of your boid that detects intersections. once the intersection has occurred along your projected vector, you can incrementally alter the course of the boid until the projected line is no longer intersecting the object. that's the basic idea.

reynolds also lays out some of his ideas for steering which are useful - simple physics concepts to constrain your boids to a maximum speed, turning velocity, etc.

if you're going with particle expressions, i'd still recommend approximating your collision objects with primitives, like spheres. that way, you're just doing the calculation from a line (your projected vector) to a point (the center of the sphere) and subtracting the radius of the sphere to test intersection. you can overlap multiple spheres and calculate your intersection to the closest one.

thinking about this again, you might be able to leverage maya's built in particle collision data, by creating a dummy particle system that leads in front of your boids and testing the collision state of the dummies. in maya7 you've got a couple of extra collision data attributes (like collisionTime) that you can test against, but i haven't tried this out.

-sunit

Smithjoe1
11-16-2006, 01:54 AM
I'd definatley be using stand in objects to make it simple, however, the code side of it puzzles me quite a bit, with what functions to use for it. I can work with vectors to some exent, the object detection flies straight past me on what to use though, perhaps I could link it with a geoconnector node from particle collisions. Its not like its as easy as projecting a vector 2 units following its velocity vector, and just running a check to see if it collides with something, collision detection isnt something I have worked with, and even just working with it and bounding sphere radiuses, I'm unsure how I could work it out to see if its inside of an area enclosed by the boxes or not, if I could work that out even, I could have the bounding a distance away from the actual objects and have them turn over 1 unit and not have to worry about proper detection

I could always just use 2 particle systems, using the 1st as a goal object for the 2nd and have them bounce around, But it's then lacking other parts of it that I'd eventually like to implement, based around the boid motion of group attraction and stuff.

sunit
11-16-2006, 04:31 AM
Its not like its as easy as projecting a vector 2 units following its velocity vector, and just running a check to see if it collides with something

if you're using stand-in spheres, you can calculate the shortest distance from the vector the center of the spheres:

http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ (http://local.wasp.uwa.edu.au/%7Epbourke/geometry/pointline/)

subtracting the radius for each spherical standin from each shortest distance will give you the distance from the surface of the sphere to the vector. if the number is negative, then you know your vector is intersecting and that your boid will collide with this spherical standin.

once you know that, you can adjust the velocity vector to avoid that sphere. this is a good paper to refer to for the locomotion:

http://www.red3d.com/cwr/papers/1987/boids.html

you can also just add an incremental amount to your velocity in the direction opposite to (or also perpendicular to) the collision (the boid's position minus the sphere's center will give you the opposite direction).

as for bounding boxes, the simplest way is the brute force one - since you know all the max/min values for the boundingBox (ex. boundingBoxMin and boundingBoxMax attrs) you can test where the boid position for each axis in relation to the boundingBox. testing whether a vector intersects a bounding box is a bit more complicated (projecting the line onto each plane of the boundingBox is one way i can think of). the spheres are the easiest case.

there're a few crowd/flock simulation scripts people have written for maya and posted up. you might want to take a look at them and see what's happening under the hood. that may give you a starting point.

-sunit

Smithjoe1
11-16-2006, 07:23 AM
Okay, so I tried to work out a possible way to calculate if it was near a polygon or not, but as far as I got it was just rewriting the formulas. However, instead of only using spheres, I can just use an outer bounding boxes verticies to map it, might get slow to calculate though.


global int $close;
int $i; int $particleCount;
global float $closeDist = 2.0; //how close it can be to detected point
string $particleObject = "particle1";
string $particleObjectArray;
string $command = ("particle -q -ct " + $particleObject);
$particleCount = `eval $command`;
//set up arrays to work around naming limitations

//loop for each particle
for ($i=0; $i <= `eval $command`; $i++){
$particleObjectArray = ($particleObject+".pt["+$i+"]");
BoxDetection("pCube1", $particleObjectArray);
};


global proc BoxDetection(string $object, string $particle)
{
global int $close = 0;
global float $closeDist;
int $vertCountArray[]; //vertex counter
int $i; int $xd; int $yd; int $zd;
int $vertCount;
float $distance;
vector $vertexLocation;
string $query;

vector $particleLocation = `xform -q -ws -t $particle`;
$vertCountArray = `polyEvaluate -v $object`;
$vertCount = $vertCountArray[0];

for ($i=0; $i<=($vertCount-1); $i++) {
$query = "xform -q -ws -t " + $object +".vtx["+ $i +"]";
//formula for distance between two 3d points
$vertexLocation = `eval $query`;

$xd = $particleLocation.x-$vertexLocation.x;
$yd = $particleLocation.y-$vertexLocation.y;
$zd = $particleLocation.z-$vertexLocation.z;
$distance = sqrt($xd*$xd + $yd*$yd + $zd*$zd);

if ($distance <= $closeDist){
$close = 1; //returns if its close to the object
};
};
};


Thats the little script I have at the moment to test it at the moment, particles inside of a cube, but it seems to break quite often though. I'll update as it scrips forward.

Its good though, because I can create a 2d array of objects, being either cubes or spheres, and use a switch and a modified switch for each of them, with the sphere one just being a centre point and radius instead of every vertex. Yes, I know its messy, I'll get around to cleaning it up sometime soon when my brain is fully up and running and can think again, I need to get rid of the vertCountArray somehow and just make it an int, not sure on what to use to do that.

Now I need to start working on how I'm going to go about making them turn around in a random direction away from the box over time.

Edit: Fixed script... Duh, forgot to calculate the vertex positions in world space.

sunit
11-16-2006, 08:54 AM
if you're writing a particle expression - you don't need to calculate for each particle in your expression. each frame, a particle expression will run through all the particles twice - before and after applying other forces such as fields.

so, if you write:


position = sphrand(1);


in a runtime particle expression, each particle position will get a random number at every frame.

if you're running a particle expression and only want to calculate, say, obstacle positions once every frame (instead of for each particle), you can also optimize your expression:


vector $obstacle_pos;
if (particleId == 0)
{
$obstacle_pos = <<obst.tx,obst.ty,obst.tz>>;
}


the variable $obstacle_pos is then adjusted for each particle including and after particleId == 0;

take a look at the documentation for particle expressions - you'll find a wealth of information about the different vector functions you have access to and the particle attributes. you can also treat particle attributes a bit like persistent variables, and create user-defined per-particle attributes to store information about your current simulation.

you'll also find that it's generally slower to call a melscript from a particle expression - instead, you're better off hard-coding the process into the particle expression.

if you're curious, i wrote a maya flocking implementation earlier this year for a workshop:

http:///sunitparekh.com/temp/fish_swarm_sor3_mid37.mov

using some of the ideas i've mentioned above and the work by craig reynolds.

-sunit

Smithjoe1
11-17-2006, 01:06 AM
int $i; float $xd; float $yd; float $zd;
int $c = 1; //number of objects in collision set - 1
int $close;
float $distance;
float $closeDist;
matrix $objects[2][2];
string $obst[];
vector $obstacle_pos[];
vector $ParticlePos;
vector $obstacle_pos_tmp;

if (particleId == 0){
//init matrix. need to automate
$obst[0]="pSphere1";
$obst[1]="pSphere2";
$objects[0][0]=0;
$objects[0][1]=2.796;
$objects[1][0]=1;
$objects[1][1]=12;

for ($i=0;$i<=$c;$i++){
$obstacle_pos[$i] = `xform -q -ws -t $obst[$i]`;
};
};
for ($i=0;$i<=$c;$i++) {
$particlePos = particleShape1.wps;
$obstacle_pos_tmp = $obstacle_pos[$i];
$xd = ($particlePos.x) - ($obstacle_pos_tmp.x);
$yd = ($particlePos.y) - ($obstacle_pos_tmp.y);
$zd = ($particlePos.z) - ($obstacle_pos_tmp.z);
$distance = sqrt($xd*$xd + $yd*$yd + $zd*$zd);
$closeDist = 2 + $objects[$i][1];

// distance + radius
if ($distance <= $midDist){
particleShape1.velocity = particleShape1.velocity + <<noise(time),noise(time/2),0>>;
if ($distance <= $closeDist){
$close = 0;
particleShape1.velocity = particleShape1.velocity * -1;
};
};
};


Thats my expression now, it works quite well for object detections, now I need to make it so they can deflect somehow, and to automatically load the objects in the collision layer without having to manually load them yourself.

The problem that I am currently having though is how to get it to curve away from the object over time, say if its current velocity vector = <<1,0,0>> how to make it choose a random direction, to eventually have a vector <<-1,.2,0>> over the course of a few frames, the problem with using a rand function for that is that it will keep jittering, and I'm not sure how I could do it to choose a new vector somewhere in the oppisite region of its current direction, and pan over there over the course of a few frames.
I'd need to assign a point of data for each particle, that doesnt get refreshed every run, unless I have it constantally zeroing while its not close to the object, and when it is, its a counter increments it, but it only calculates the direction on the first run of the counter. Might work

tbaypaul
11-17-2006, 01:16 AM
resources for this can also be found in the melBots dvd, the mel Fundamentals book(minor references) and Mel Scripting for Maya Animators(many pages on such behaviours)..by Mark Wilkins et al...

Smithjoe1
11-20-2006, 01:02 AM
Sigh, more code


//variables for collision detection
int $i; float $xd; float $yd; float $zd;
int $c = 1; //number of objects in collision set - 1
int $close = 0;
int $count[];
int $steps = 1;
int $tmpInt;
int $pi = particleId;
float $x; float $y; float $z;
float $distance;
float $closeDist;
int $colidedObject[];
matrix $objects[2][2];
string $obst[];
vector $temp1;
vector $temp2;
vector $newDir[];
vector $directions[];
vector $obstacle_pos[];
vector $ParticlePos;
vector $obstacle_pos_tmp;
//variables for locator direction
vector $locatorPosition;
float $tv;
vector $uv;
vector $speed = sphrand(<<1,1,1>>);

if (abs(particleShape1.position - $locatorPosition) > <<1,1,1>>) {

$tv = mag(particleShape1.position - $locatorPosition);
$tv = $tv - <<5,5,5>>;
$uv = unit(particleShape1.position - $locatorPosition);
particleShape1.acceleration = particleShape1.acceleration - (0.5 * $tv * $uv);
} else {
particleShape1.acceleration = -$speed;
}

//run before each frame
if (particleId == 0){
$locatorPosition = `xform -q -ws -t locator1`;
//init matrix. need to automate
$obst[0]="pSphere1";
$obst[1]="pSphere2";
$objects[0][0]=0;
$objects[0][1]=12;
$objects[1][0]=1;
$objects[1][1]=12;

for ($i=0;$i<=$c;$i++){
$obstacle_pos[$i] = `xform -q -ws -t $obst[$i]`;
};
};

for ($i=0;$i<=$c;$i++) {
$particlePos = particleShape1.wps;
$obstacle_pos_tmp = $obstacle_pos[$i];
$xd = ($particlePos.x) - ($obstacle_pos_tmp.x);
$yd = ($particlePos.y) - ($obstacle_pos_tmp.y);
$zd = ($particlePos.z) - ($obstacle_pos_tmp.z);
$distance = sqrt($xd*$xd + $yd*$yd + $zd*$zd);
$closeDist = 2 + $objects[$i][1];
if ($distance <= $closeDist){
$close = 1;
$colidedObject[$pi] = $i;
}
};

if ($close == 1) {
$count[$pi] = $count[$pi] + 1;
if ($count[$pi] == 1) {
$tmpInt = $colidedObject[$pi];
$temp1 = particleShape1.wps - $obstacle_pos[$tmpInt];
$newDir[$pi] = $temp1;
$temp1 = particleShape1.velocity;
$temp2 = $newDir[$pi];
$x = ($temp2.x + $temp1.x)/$steps;
$y = ($temp2.y + $temp1.y)/$steps;
$z = ($temp2.z + $temp1.z)/$steps;
$directions[$pi] = <<$x,$y,$z>>;
}
if ($newDir[$pi] != particleShape1.velocity) {
$temp1 = $directions[$pi];
particleShape1.acceleration = particleShape1.acceleration + <<$temp1.x,$temp1.y,$temp1.z>>;
}
} else {
$count[$pi] = 0;
$colidedObject[$pi] = 0;
};


Okay, so I've managed to get the collisions out, and have it change the vector, Trying to make it do it over a series of frames, as defined by $steps. I couldnt work out a suitable way to make it work onto a perpendicular vector, everytime I tried to use the cross(vector,vector) function, they'd shoot off perpendicular but way too fast, I'll have to control that somehow.

Thanks for the help sofar guys:)

Edit, just working with the flock script for basic locator directions helps a lot of it finding going around the object, but with every collision it causes it to speed up immensley, and if the particle is going too quick, then it cuts through the object

Is there any code to limit the particles velocity? And is there any better way than using a cross between between the current vector and a user defined one to create a bounce that isnt oppisite, just a bounce at an angle?

CGTalk Moderation
11-20-2006, 01:02 AM
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.