src/thirdparty/particles/ParticleDLL/actions.cpp

Go to the documentation of this file.
00001 // actions.cpp
00002 //
00003 // Copyright 1997-2006 by David K. McAllister
00004 //
00005 // I used code Copyright 1997 by Jonathan P. Leech
00006 // as an example in implenting this.
00007 //
00008 // This file implements the dynamics of particle actions.
00009 
00010 #include "actions.h"
00011 #include "ParticleState.h"
00012 
00013 #include <algorithm>
00014 // For dumping errors
00015 #include <sstream>
00016 #include <typeinfo>
00017 
00018 // Compute the inverse matrix of the plane basis.
00019 static inline void NewBasis(const pVec &u, const pVec &v, pVec &s1, pVec &s2)
00020 {
00021     pVec w = Cross(u, v);
00022 
00023     float det = 1.0f / (w.z()*u.x()*v.y() - w.z()*u.y()*v.x() - u.z()*w.x()*v.y() - u.x()*v.z()*w.y() + v.z()*w.x()*u.y() + u.z()*v.x()*w.y());
00024 
00025     s1 = pVec((v.y()*w.z() - v.z()*w.y()), (v.z()*w.x() - v.x()*w.z()), (v.x()*w.y() - v.y()*w.x()));
00026     s1 *= det;
00027     s2 = pVec((u.y()*w.z() - u.z()*w.y()), (u.z()*w.x() - u.x()*w.z()), (u.x()*w.y() - u.y()*w.x()));
00028     s2 *= -det;
00029 }
00030 
00031 void PAAvoid::Exec(const PDTriangle &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00032 {
00033     float magdt = magnitude * dt;
00034 
00035     const pVec &u = dom.u;
00036     const pVec &v = dom.v;
00037 
00038     // The normalized bases are needed inside the loop.
00039     const pVec &un = dom.uNrm;
00040     const pVec &vn = dom.vNrm;
00041 
00042     // f is the third (non-basis) triangle edge.
00043     pVec f = v - u;
00044     pVec fn = f;
00045     fn.normalize();
00046 
00047     // Compute the inverse matrix of the plane basis.
00048     pVec s1, s2;
00049     NewBasis(u, v, s1, s2);
00050 
00051     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00052         Particle &m = (*it);
00053 
00054         // See if particle's current and look_ahead positions cross plane.
00055         // If not, couldn't hit, so keep going.
00056         pVec pnext = m.pos + m.vel * dt * look_ahead;
00057 
00058         // nrm stores the plane normal (the a,b,c of the plane eqn).
00059         // Old and new distances: dist(p,plane) = n * p + d
00060         float distold = m.pos * dom.nrm + dom.D;
00061         float distnew = pnext * dom.nrm + dom.D;
00062 
00063         if(pSameSign(distold, distnew))
00064             continue;
00065 
00066         float nv = dom.nrm * m.vel;
00067         float t = -distold / nv; // Time steps before hit
00068 
00069         pVec phit = m.pos + m.vel * t; // Actual intersection point
00070         pVec offset = phit - dom.p; // Offset from origin in plane
00071 
00072         // Dot product with basis vectors of old frame
00073         // in terms of new frame gives position in uv frame.
00074         float upos = offset * s1;
00075         float vpos = offset * s2;
00076 
00077         // Did it cross plane outside triangle?
00078         if(upos < 0 || vpos < 0 || (upos + vpos) > 1)
00079             continue;
00080 
00081         // A hit! A most palpable hit!
00082         // Compute distance to the three edges.
00083         pVec uofs = (un * (un * offset)) - offset;
00084         float udistSqr = uofs.length2();
00085         pVec vofs = (vn * (vn * offset)) - offset;
00086         float vdistSqr = vofs.length2();
00087 
00088         pVec foffset = offset - u;
00089         pVec fofs = (fn * (fn * foffset)) - foffset;
00090         float fdistSqr = fofs.length2();
00091 
00092         // S is the safety vector toward the closest point on boundary.
00093         pVec S;
00094         if(udistSqr <= vdistSqr && udistSqr <= fdistSqr) S = uofs;
00095         else if(vdistSqr <= fdistSqr) S = vofs;
00096         else S = fofs;
00097 
00098         // Blend S with m.vel.
00099         S.normalize();
00100 
00101         float vm = m.vel.length();
00102         pVec Vn = m.vel / vm;
00103 
00104         pVec dir = (S * (magdt / (fsqr(t)+epsilon))) + Vn;
00105         m.vel = dir * (vm / dir.length()); // Speed of m.vel, but in direction dir.
00106     }
00107 }
00108 
00109 void PAAvoid::Exec(const PDRectangle &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00110 {
00111     float magdt = magnitude * dt;
00112 
00113     const pVec &u = dom.u;
00114     const pVec &v = dom.v;
00115 
00116     // The normalized bases are needed inside the loop.
00117     const pVec &un = dom.uNrm;
00118     const pVec &vn = dom.vNrm;
00119 
00120     // Compute the inverse matrix of the plane basis.
00121     pVec s1, s2;
00122     NewBasis(u, v, s1, s2);
00123 
00124     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00125         Particle &m = (*it);
00126 
00127         // See if particle's current and look_ahead positions cross plane.
00128         // If not, couldn't hit, so keep going.
00129         pVec pnext = m.pos + m.vel * dt * look_ahead;
00130 
00131         // nrm stores the plane normal (the a,b,c of the plane eqn).
00132         // Old and new distances: dist(p,plane) = n * p + d
00133         float distold = m.pos * dom.nrm + dom.D;
00134         float distnew = pnext * dom.nrm + dom.D;
00135 
00136         if(pSameSign(distold, distnew))
00137             continue;
00138 
00139         float nv = dom.nrm * m.vel;
00140         float t = -distold / nv;
00141 
00142         pVec phit = m.pos + m.vel * t; // Actual intersection point
00143         pVec offset = phit - dom.p; // Offset from origin in plane
00144 
00145         // Dot product with basis vectors of old frame
00146         // in terms of new frame gives position in uv frame.
00147         float upos = offset * s1;
00148         float vpos = offset * s2;
00149 
00150         // Did it cross plane outside rectangle?
00151         if(upos < 0 || vpos < 0 || upos > 1 || vpos > 1)
00152             continue;
00153 
00154         // A hit! A most palpable hit!
00155         // Compute distance to the three edges.
00156         pVec uofs = (un * (un * offset)) - offset;
00157         float udistSqr = uofs.length2();
00158         pVec vofs = (vn * (vn * offset)) - offset;
00159         float vdistSqr = vofs.length2();
00160 
00161         pVec foffset = (u + v) - offset;
00162         pVec fofs = (un * (un * foffset)) - foffset;
00163         float fdistSqr = fofs.length2();
00164         pVec gofs = (un * (un * foffset)) - foffset;
00165         float gdistSqr = gofs.length2();
00166 
00167         pVec S;
00168         if(udistSqr <= vdistSqr && udistSqr <= fdistSqr
00169                 && udistSqr <= gdistSqr) S = uofs;
00170         else if(vdistSqr <= fdistSqr && vdistSqr <= gdistSqr) S = vofs;
00171         else if(fdistSqr <= gdistSqr) S = fofs;
00172         else S = gofs;
00173 
00174         // Blend S with m.vel.
00175         S.normalize();
00176 
00177         float vm = m.vel.length();
00178         pVec Vn = m.vel / vm;
00179 
00180         pVec dir = (S * (magdt / (fsqr(t)+epsilon))) + Vn;
00181         m.vel = dir * (vm / dir.length()); // Speed of m.vel, but in direction dir.
00182     }
00183 }
00184 
00185 void PAAvoid::Exec(const PDPlane &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00186 {
00187     float magdt = magnitude * dt;
00188 
00189     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00190         Particle &m = (*it);
00191 
00192         // See if particle's current and look_ahead positions cross plane.
00193         // If not, couldn't hit, so keep going.
00194         pVec pnext = m.pos + m.vel * dt * look_ahead;
00195 
00196         // nrm stores the plane normal (the a,b,c of the plane eqn).
00197         // Old and new distances: dist(p,plane) = n * p + d
00198         float distold = m.pos * dom.nrm + dom.D;
00199         float distnew = pnext * dom.nrm + dom.D;
00200 
00201         if(pSameSign(distold, distnew))
00202             continue;
00203 
00204         float nv = dom.nrm * m.vel;
00205         float t = -distold / nv;
00206 
00207         // Blend S with m.vel.
00208         const pVec &S = dom.nrm;
00209 
00210         float vm = m.vel.length();
00211         pVec Vn = m.vel / vm;
00212 
00213         pVec dir = (S * (magdt / (fsqr(t)+epsilon))) + Vn;
00214         m.vel = dir * (vm / dir.length()); // Speed of m.vel, but in direction dir.
00215     }
00216 }
00217 
00218 // Only works for points on the OUTSIDE of the sphere. Ignores inner radius.
00219 void PAAvoid::Exec(const PDSphere &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00220 {
00221     float magdt = magnitude * dt;
00222 
00223     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00224         Particle &m = (*it);
00225 
00226         // First do a ray-sphere intersection test and see if it's soon enough.
00227         // Can I do this faster without t?
00228         float vm = m.vel.length();
00229         pVec Vn = m.vel / vm;
00230 
00231         pVec L = dom.ctr - m.pos;
00232         float v = L * Vn;
00233 
00234         float disc = dom.radOutSqr - (L * L) + fsqr(v);
00235         if(disc < 0)
00236             continue; // I'm not heading toward it.
00237 
00238         // Compute length for second rejection test.
00239         float t = v - sqrtf(disc);
00240         if(t < 0 || t > (vm * look_ahead))
00241             continue;
00242 
00243         // Get a vector to safety.
00244         pVec C = Cross(Vn, L);
00245         C.normalize();
00246         pVec S = Cross(Vn, C);
00247 
00248         pVec dir = (S * (magdt / (fsqr(t)+epsilon))) + Vn;
00249         m.vel = dir * (vm / dir.length()); // Speed of m.vel, but in direction dir.
00250     }
00251 }
00252 
00253 void PAAvoid::Exec(const PDDisc &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00254 {
00255     float magdt = magnitude * dt;
00256 
00257     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00258         Particle &m = (*it);
00259 
00260         // See if particle's current and look_ahead positions cross plane.
00261         // If not, couldn't hit, so keep going.
00262         pVec pnext = m.pos + m.vel * dt * look_ahead;
00263 
00264         // nrm stores the plane normal (the a,b,c of the plane eqn).
00265         // Old and new distances: dist(p,plane) = n * p + d
00266         float distold = m.pos * dom.nrm + dom.D;
00267         float distnew = pnext * dom.nrm + dom.D;
00268 
00269         if(pSameSign(distold, distnew))
00270             continue;
00271 
00272         float nv = dom.nrm * m.vel;
00273         float t = -distold / nv;
00274 
00275         pVec phit = m.pos + m.vel * t; // Actual intersection point
00276         pVec offset = phit - dom.p; // Offset from origin in plane
00277 
00278         float radSqr = offset.length2();
00279 
00280         // Are we going to hit the disc ring? If so, always turn to the OUTSIDE of the ring.
00281         if(radSqr < dom.radInSqr || radSqr > dom.radOutSqr)
00282             continue;
00283 
00284         // Blend S with m.vel.
00285         pVec S = offset;
00286 
00287         float vm = m.vel.length();
00288         pVec Vn = m.vel / vm;
00289 
00290         pVec dir = (S * (magdt / (fsqr(t)+epsilon))) + Vn;
00291         m.vel = dir * (vm / dir.length()); // Speed of m.vel, but in direction dir.
00292     }
00293 }
00294 
00295 void PAAvoid::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00296 {
00297     if(typeid(*position) == typeid(PDTriangle)) {
00298         Exec(*dynamic_cast<const PDTriangle *>(position), group, ibegin, iend);
00299     } else if(typeid(*position) == typeid(PDDisc)) {
00300         Exec(*dynamic_cast<const PDDisc *>(position), group, ibegin, iend);
00301     } else if(typeid(*position) == typeid(PDPlane)) {
00302         Exec(*dynamic_cast<const PDPlane *>(position), group, ibegin, iend);
00303     } else if(typeid(*position) == typeid(PDRectangle)) {
00304         Exec(*dynamic_cast<const PDRectangle *>(position), group, ibegin, iend);
00305     } else if(typeid(*position) == typeid(PDSphere)) {
00306         Exec(*dynamic_cast<const PDSphere *>(position), group, ibegin, iend);
00307     } else {
00308         std::string err = std::string("pAvoid not implemented for domain ") + std::string(typeid(*position).name());
00309         _GetPState().SetError(PERR_NOT_IMPLEMENTED, err);
00310     }
00311 }
00312 
00313 void PABounce::Exec(const PDTriangle &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00314 {
00315     pVec s1, s2;
00316     NewBasis(dom.u, dom.v, s1, s2);
00317 
00318     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00319         Particle &m = (*it);
00320 
00321         // See if particle's current and look_ahead positions cross plane.
00322         // If not, couldn't hit, so keep going.
00323         pVec pnext = m.pos + m.vel * dt;
00324 
00325         // nrm stores the plane normal (the a,b,c of the plane eqn).
00326         // Old and new distances: dist(p,plane) = n * p + d
00327         float distold = m.pos * dom.nrm + dom.D;
00328         float distnew = pnext * dom.nrm + dom.D;
00329 
00330         if(pSameSign(distold, distnew))
00331             continue;
00332 
00333         float nv = dom.nrm * m.vel;
00334         float t = -distold / nv; // Time steps before hit
00335 
00336         pVec phit = m.pos + m.vel * t; // Actual intersection point
00337         pVec offset = phit - dom.p; // Offset from origin in plane
00338 
00339         // Dot product with basis vectors of old frame
00340         // in terms of new frame gives position in uv frame.
00341         float upos = offset * s1;
00342         float vpos = offset * s2;
00343 
00344         // Did it cross plane outside triangle?
00345         if(upos < 0 || vpos < 0 || (upos + vpos) > 1)
00346             continue;
00347 
00348         // A hit! A most palpable hit!
00349         // Compute tangential and normal components of velocity
00350         pVec vn = dom.nrm * nv; // Normal Vn = (V.N)N
00351         pVec vt = m.vel - vn;   // Tangent Vt = V - Vn
00352 
00353         // Compute new velocity heading out:
00354         // Don't apply friction if tangential velocity < cutoff
00355         if(vt.length2() <= cutoffSqr)
00356             m.vel = vt - vn * resilience;
00357         else
00358             m.vel = vt * oneMinusFriction - vn * resilience;
00359     }
00360 }
00361 
00362 void PABounce::Exec(const PDRectangle &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00363 {
00364     pVec s1, s2;
00365     NewBasis(dom.u, dom.v, s1, s2);
00366 
00367     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00368         Particle &m = (*it);
00369 
00370         // See if particle's current and pnext positions cross plane.
00371         // If not, couldn't hit, so keep going.
00372         pVec pnext = m.pos + m.vel * dt;
00373 
00374         // nrm stores the plane normal (the a,b,c of the plane eqn).
00375         // Old and new distances: dist(p,plane) = n * p + d
00376         float distold = m.pos * dom.nrm + dom.D;
00377         float distnew = pnext * dom.nrm + dom.D;
00378 
00379         if(pSameSign(distold, distnew))
00380             continue;
00381 
00382         float nv = dom.nrm * m.vel;
00383         float t = -distold / nv; // Time steps before hit
00384 
00385         pVec phit = m.pos + m.vel * t; // Actual intersection point
00386         pVec offset = phit - dom.p; // Offset from origin in plane
00387 
00388         // Dot product with basis vectors of old frame
00389         // in terms of new frame gives position in uv frame.
00390         float upos = offset * s1;
00391         float vpos = offset * s2;
00392 
00393         // Did it cross plane outside rectangle?
00394         if(upos < 0 || upos > 1 || vpos < 0 || vpos > 1)
00395             continue;
00396 
00397         // A hit! A most palpable hit!
00398         // Compute tangential and normal components of velocity
00399         pVec vn = dom.nrm * nv; // Normal Vn = (V.N)N
00400         pVec vt = m.vel - vn;   // Tangent Vt = V - Vn
00401 
00402         // Compute new velocity heading out:
00403         // Don't apply friction if tangential velocity < cutoff
00404         if(vt.length2() <= cutoffSqr)
00405             m.vel = vt - vn * resilience;
00406         else
00407             m.vel = vt * oneMinusFriction - vn * resilience;
00408     }
00409 }
00410 
00411 void PABounce::Exec(const PDPlane &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00412 {
00413     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00414         Particle &m = (*it);
00415 
00416         // See if particle's current and look_ahead positions cross plane.
00417         // If not, couldn't hit, so keep going.
00418         pVec pnext = m.pos + m.vel * dt;
00419 
00420         // nrm stores the plane normal (the a,b,c of the plane eqn).
00421         // Old and new distances: dist(p,plane) = n * p + d
00422         float distold = m.pos * dom.nrm + dom.D;
00423         float distnew = pnext * dom.nrm + dom.D;
00424 
00425         if(pSameSign(distold, distnew))
00426             continue;
00427 
00428         float nv = dom.nrm * m.vel;
00429         // float t = -distold / nv; // Time steps before hit
00430 
00431         // A hit! A most palpable hit!
00432         // Compute tangential and normal components of velocity
00433         pVec vn = dom.nrm * nv; // Normal Vn = (V.N)N
00434         pVec vt = m.vel - vn;   // Tangent Vt = V - Vn
00435 
00436         // Compute new velocity heading out:
00437         // Don't apply friction if tangential velocity < cutoff
00438         if(vt.length2() <= cutoffSqr)
00439             m.vel = vt - vn * resilience;
00440         else
00441             m.vel = vt * oneMinusFriction - vn * resilience;
00442     }
00443 }
00444 
00445 void PABounce::Exec(const PDSphere &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00446 {
00447     PASSERT(dom.radIn == 0.0f, "Bouncing doesn't work on thick shells. radIn must be 0.");
00448 
00449     float dtinv = 1.0f / dt;
00450 
00451     // Bounce particles off the inside or outside of the sphere
00452     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00453         Particle &m = (*it);
00454 
00455         // See if particle's next position is on the opposite side of the domain. If so, bounce it.
00456         pVec pnext = m.pos + m.vel * dt;
00457 
00458         if(dom.Within(m.pos)) {
00459             // We are bouncing off the inside of the sphere.
00460             if(dom.Within(pnext))
00461                 // Still inside. Do nothing.
00462                 continue;
00463 
00464             // Trying to go outside. Bounce back in.
00465 
00466             // Inward-pointing normal to surface. This isn't computed quite right;
00467             // should extrapolate particle position to surface.
00468             pVec n(dom.ctr - m.pos);
00469             n.normalize();
00470 
00471             // Compute tangential and normal components of velocity
00472             float nmag = m.vel * n;
00473 
00474             pVec vn = n * nmag;   // Velocity in Normal dir  Vn = (V.N)N
00475             pVec vt = m.vel - vn; // Velocity in Tangent dir Vt = V - Vn
00476 
00477             // Reverse normal component of velocity
00478             if(nmag < 0) vn = -vn; // Don't reverse if it's already heading inward
00479 
00480             // Compute new velocity heading out:
00481             // Don't apply friction if tangential velocity < cutoff
00482             float tanscale = (vt.length2() <= cutoffSqr) ? 1.0f : oneMinusFriction;
00483             m.vel = vt * tanscale + vn * resilience;
00484 
00485             // Now see where the point will end up. Make sure we fixed it to stay inside.
00486             pVec pthree = m.pos + m.vel * dt;
00487             if(dom.Within(pthree)) {
00488                 // Still inside. We're good.
00489                 continue;
00490             } else {
00491                 // Since the tangent plane is outside the sphere, reflecting the velocity vector about it won't necessarily bring it inside the sphere.
00492                 pVec toctr = dom.ctr - pthree;
00493                 float dist = toctr.length();
00494                 pVec pwish = dom.ctr - toctr * (0.999f * dom.radOut / dist); // pwish is a point just inside the sphere
00495                 m.vel = (pwish - m.pos) * dtinv; // Compute a velocity to get us to pwish.
00496             }
00497         } else {
00498             // We are bouncing off the outside of the sphere.
00499             if(!dom.Within(pnext))
00500                 continue;
00501 
00502             // Trying to go inside. Bounce back out.
00503 
00504             // Outward-pointing normal to surface. This isn't computed quite right;
00505             // should extrapolate particle position to surface.
00506             pVec n = m.pos - dom.ctr;
00507             n.normalize();
00508 
00509             // Compute tangential and normal components of velocity
00510             float nmag = m.vel * n;
00511 
00512             pVec vn = n * nmag;   // Velocity in Normal dir  Vn = (V.N)N
00513             pVec vt = m.vel - vn; // Velocity in Tangent dir Vt = V - Vn
00514 
00515             // Reverse normal component of velocity if it points in
00516             if(nmag < 0)
00517                 vn = -vn;
00518 
00519             // Compute new velocity heading out:
00520             // Don't apply friction if tangential velocity < cutoff
00521             float tanscale = (vt.length2() <= cutoffSqr) ? 1.0f : oneMinusFriction;
00522             m.vel = vt * tanscale + vn * resilience;
00523         }
00524     }
00525 }
00526 
00527 void PABounce::Exec(const PDDisc &dom, ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00528 {
00529     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00530         Particle &m = (*it);
00531 
00532         // See if particle's current and look_ahead positions cross plane.
00533         // If not, couldn't hit, so keep going.
00534         pVec pnext = m.pos + m.vel * dt;
00535 
00536         // nrm stores the plane normal (the a,b,c of the plane eqn).
00537         // Old and new distances: dist(p,plane) = n * p + d
00538         float distold = m.pos * dom.nrm + dom.D;
00539         float distnew = pnext * dom.nrm + dom.D;
00540 
00541         if(pSameSign(distold, distnew))
00542             continue;
00543 
00544         float nv = dom.nrm * m.vel;
00545         float t = -distold / nv; // Time steps before hit
00546 
00547         pVec phit = m.pos + m.vel * t; // Actual intersection point
00548         pVec offset = phit - dom.p; // Offset from origin in plane
00549 
00550         float radSqr = offset.length2();
00551 
00552         // Are we going to hit the disc ring? If so, always turn to the OUTSIDE of the ring.
00553         if(radSqr < dom.radInSqr || radSqr > dom.radOutSqr)
00554             continue;
00555 
00556         // A hit! A most palpable hit!
00557         // Compute tangential and normal components of velocity
00558         pVec vn = dom.nrm * nv; // Normal Vn = (V.N)N
00559         pVec vt = m.vel - vn;   // Tangent Vt = V - Vn
00560 
00561         // Compute new velocity heading out:
00562         // Don't apply friction if tangential velocity < cutoff
00563         if(vt.length2() <= cutoffSqr)
00564             m.vel = vt - vn * resilience;
00565         else
00566             m.vel = vt * oneMinusFriction - vn * resilience;
00567     }
00568 }
00569 
00570 void PABounce::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00571 {
00572     if(typeid(*position) == typeid(PDTriangle)) {
00573         Exec(*dynamic_cast<const PDTriangle *>(position), group, ibegin, iend);
00574     } else if(typeid(*position) == typeid(PDDisc)) {
00575         Exec(*dynamic_cast<const PDDisc *>(position), group, ibegin, iend);
00576     } else if(typeid(*position) == typeid(PDPlane)) {
00577         Exec(*dynamic_cast<const PDPlane *>(position), group, ibegin, iend);
00578     } else if(typeid(*position) == typeid(PDRectangle)) {
00579         Exec(*dynamic_cast<const PDRectangle *>(position), group, ibegin, iend);
00580     } else if(typeid(*position) == typeid(PDSphere)) {
00581         Exec(*dynamic_cast<const PDSphere *>(position), group, ibegin, iend);
00582     } else {
00583         std::string err = std::string("pBounce not implemented for domain ") + std::string(typeid(*position).name());
00584         _GetPState().SetError(PERR_NOT_IMPLEMENTED, err);
00585     }
00586 }
00587 
00588 // An action list within an action list
00589 void PACallActionList::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00590 {
00591     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
00592     pCallActionList(action_list_num);
00593 }
00594 
00595 // Set the secondary position and velocity from current.
00596 void PACopyVertexB::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00597 {
00598     if(copy_pos && copy_vel) {
00599         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00600             Particle &m = (*it);
00601             m.posB = m.pos;
00602             m.velB = m.vel;
00603         }
00604     } else if(copy_pos) {
00605         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00606             Particle &m = (*it);
00607             m.posB = m.pos;
00608         }
00609     } else if(copy_vel) {
00610         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00611             Particle &m = (*it);
00612             m.velB = m.vel;
00613             m.rvelB = m.rvel;
00614         }
00615     }
00616 }
00617 
00618 // Dampen velocities
00619 void PADamping::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00620 {
00621     // This is important if dt is != 1.
00622     pVec one(1,1,1);
00623     pVec scale(one - ((one - damping) * dt));
00624 
00625     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00626         Particle &m = (*it);
00627         float vSqr = m.vel.length2();
00628 
00629         if(vSqr >= vlowSqr && vSqr <= vhighSqr) {
00630             m.vel = CompMult(m.vel, scale);
00631         }
00632     }
00633 }
00634 
00635 // Dampen rotational velocities
00636 void PARotDamping::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00637 {
00638     // This is important if dt is != 1.
00639     pVec one(1,1,1);
00640     pVec scale(one - ((one - damping) * dt));
00641 
00642     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00643         Particle &m = (*it);
00644         float vSqr = m.rvel.length2();
00645 
00646         if(vSqr >= vlowSqr && vSqr <= vhighSqr) {
00647             m.rvel = CompMult(m.rvel, scale);
00648         }
00649     }
00650 }
00651 
00652 // Exert force on each particle away from explosion center
00653 void PAExplosion::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00654 {
00655     float radius = velocity * age;
00656     float magdt = magnitude * dt;
00657     float oneOverSigma = 1.0f / stdev;
00658     float inexp = -0.5f*fsqr(oneOverSigma);
00659     float outexp = P_ONEOVERSQRT2PI * oneOverSigma;
00660 
00661     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00662         Particle &m = (*it);
00663 
00664         // Figure direction to particle.
00665         pVec dir(m.pos - center);
00666         float distSqr = dir.length2();
00667         float dist = sqrtf(distSqr);
00668         float DistFromWaveSqr = fsqr(radius - dist);
00669 
00670         float Gd = exp(DistFromWaveSqr * inexp) * outexp;
00671         pVec amount = dir * (Gd * magdt / (dist * (distSqr + epsilon)));
00672 
00673         m.vel += amount;
00674     }
00675 
00676     age += dt;
00677 }
00678 
00679 // Follow the next particle in the list
00680 void PAFollow::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00681 {
00682     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
00683 
00684     float magdt = magnitude * dt;
00685     float max_radiusSqr = fsqr(max_radius);
00686 
00687     if (group.size() < 2)
00688         return;
00689 
00690     ParticleList::iterator end = iend;
00691     end--;
00692 
00693     if(max_radiusSqr < P_MAXFLOAT) {
00694         for (ParticleList::iterator it = ibegin; it != end; it++) {
00695             Particle &m = (*it);
00696             ParticleList::iterator next = it;
00697             next++;
00698 
00699             // Accelerate toward the particle after me in the list.
00700             pVec tohim((*next).pos - m.pos); // tohim = p1 - p0
00701             float tohimlenSqr = tohim.length2();
00702 
00703             if(tohimlenSqr < max_radiusSqr) {
00704                 // Compute force exerted between the two bodies
00705                 m.vel += tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon)));
00706             }
00707         }
00708     } else {
00709         // If not using radius cutoff, avoid the if().
00710         for (ParticleList::iterator it = ibegin; it != end; it++) {
00711             Particle &m = (*it);
00712             ParticleList::iterator next = it;
00713             next++;
00714 
00715             // Accelerate toward the particle after me in the list.
00716             pVec tohim((*next).pos - m.pos); // tohim = p1 - p0
00717             float tohimlenSqr = tohim.length2();
00718 
00719             // Compute force exerted between the two bodies
00720             m.vel += tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon)));
00721         }
00722     }
00723 }
00724 
00725 // Inter-particle gravitation
00726 void PAGravitate::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00727 {
00728     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
00729 
00730     float magdt = magnitude * dt;
00731     float max_radiusSqr = max_radius * max_radius;
00732 
00733     if(max_radiusSqr < P_MAXFLOAT) {
00734         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00735             Particle &m = (*it);
00736 
00737             ParticleList::iterator j = it;
00738             j++;
00739 
00740             // Add interactions with other particles
00741             for(; j != iend; ++j) {
00742                 Particle &mj = (*j);
00743 
00744                 pVec tohim(mj.pos - m.pos); // tohim = p1 - p0
00745                 float tohimlenSqr = tohim.length2();
00746 
00747                 if(tohimlenSqr < max_radiusSqr) {
00748                     // Compute force exerted between the two bodies
00749                     pVec acc(tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon))));
00750 
00751                     m.vel += acc;
00752                     mj.vel -= acc;
00753                 }
00754             }
00755         }
00756     } else {
00757         // If not using radius cutoff, avoid the if().
00758         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00759             Particle &m = (*it);
00760 
00761             // Add interactions with other particles
00762             ParticleList::iterator j = it;
00763             ++j;
00764 
00765             for(; j != iend; ++j) {
00766                 Particle &mj = (*j);
00767 
00768                 pVec tohim(mj.pos - m.pos); // tohim = p1 - p0
00769                 float tohimlenSqr = tohim.length2();
00770 
00771                 // Compute force exerted between the two bodies
00772                 pVec acc(tohim * (magdt / (sqrtf(tohimlenSqr) * (tohimlenSqr + epsilon))));
00773 
00774                 m.vel += acc;
00775                 mj.vel -= acc;
00776             }
00777         }
00778     }
00779 }
00780 
00781 // Acceleration in a constant direction
00782 void PAGravity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00783 {
00784     pVec ddir(direction * dt);
00785 
00786     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00787         Particle &m = (*it);
00788         // Step velocity with acceleration
00789         m.vel += ddir;
00790     }
00791 }
00792 
00793 // For particles in the domain of influence, accelerate them with a domain.
00794 void PAJet::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00795 {
00796     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00797         Particle &m = (*it);
00798 
00799         if(dom->Within(m.pos)) {
00800             pVec accel = acc->Generate();
00801 
00802             // Step velocity with acceleration
00803             m.vel += accel * dt;
00804         }
00805     }
00806 }
00807 
00808 // Get rid of older particles
00809 void PAKillOld::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00810 {
00811     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
00812 
00813     // Must traverse list carefully so Remove will work
00814     for (ParticleList::iterator it = ibegin; it != group.end(); ) {
00815         Particle &m = (*it);
00816 
00817         if(!((m.age < age_limit) ^ kill_less_than))
00818             group.Remove(it);
00819         else
00820             it++;
00821     }
00822 }
00823 
00824 // Match velocity to near neighbors
00825 void PAMatchVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00826 {
00827     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
00828 
00829     float magdt = magnitude * dt;
00830     float max_radiusSqr = max_radius * max_radius;
00831 
00832     if(max_radiusSqr < P_MAXFLOAT) {
00833         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00834             Particle &m = (*it);
00835 
00836             // Add interactions with other particles
00837             ParticleList::iterator j = it;
00838             j++;
00839 
00840             // Add interactions with other particles
00841             for(; j != iend; ++j) {
00842                 Particle &mj = (*j);
00843 
00844                 pVec tohim(mj.pos - m.pos); // tohim = p1 - p0
00845                 float tohimlenSqr = tohim.length2();
00846 
00847                 if(tohimlenSqr < max_radiusSqr) {
00848                     // Compute force exerted between the two bodies
00849                     pVec acc(mj.vel * (magdt / (tohimlenSqr + epsilon)));
00850 
00851                     m.vel += acc;
00852                     mj.vel -= acc;
00853                 }
00854             }
00855         }
00856     } else {
00857         // If not using radius cutoff, avoid the if().
00858         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00859             Particle &m = (*it);
00860 
00861             // Add interactions with other particles
00862             ParticleList::iterator j = it;
00863             j++;
00864 
00865             // Add interactions with other particles
00866             for(; j != iend; ++j) {
00867                 Particle &mj = (*j);
00868 
00869                 pVec tohim(mj.pos - m.pos); // tohim = p1 - p0
00870                 float tohimlenSqr = tohim.length2();
00871 
00872                 // Compute force exerted between the two bodies
00873                 pVec acc(mj.vel * (magdt / (tohimlenSqr + epsilon)));
00874 
00875                 m.vel += acc;
00876                 mj.vel -= acc;
00877             }
00878         }
00879     }
00880 }
00881 
00882 // Match Rotational velocity to near neighbors
00883 void PAMatchRotVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00884 {
00885     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
00886 
00887     float magdt = magnitude * dt;
00888     float max_radiusSqr = max_radius * max_radius;
00889 
00890     if(max_radiusSqr < P_MAXFLOAT) {
00891         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00892             Particle &m = (*it);
00893 
00894             // Add interactions with other particles
00895             ParticleList::iterator j = it;
00896             j++;
00897 
00898             // Add interactions with other particles
00899             for(; j != iend; ++j) {
00900                 Particle &mj = (*j);
00901 
00902                 pVec tohim(mj.pos - m.pos); // tohim = p1 - p0
00903                 float tohimlenSqr = tohim.length2();
00904 
00905                 if(tohimlenSqr < max_radiusSqr) {
00906                     // Compute force exerted between the two bodies
00907                     pVec acc(mj.rvel * (magdt / (tohimlenSqr + epsilon)));
00908 
00909                     m.rvel += acc;
00910                     mj.rvel -= acc;
00911                 }
00912             }
00913         }
00914     } else {
00915         // If not using radius cutoff, avoid the if().
00916         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00917             Particle &m = (*it);
00918 
00919             // Add interactions with other particles
00920             ParticleList::iterator j = it;
00921             j++;
00922 
00923             // Add interactions with other particles
00924             for(; j != iend; ++j) {
00925                 Particle &mj = (*j);
00926 
00927                 pVec tohim(mj.pos - m.pos); // tohim = p1 - p0
00928                 float tohimlenSqr = tohim.length2();
00929 
00930                 // Compute force exerted between the two bodies
00931                 pVec acc(mj.rvel * (magdt / (tohimlenSqr + epsilon)));
00932 
00933                 m.rvel += acc;
00934                 mj.rvel -= acc;
00935             }
00936         }
00937     }
00938 }
00939 
00940 void PAMove::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00941 {
00942     // Step particle positions forward by dt, and age the particles.
00943     for (ParticleList::iterator it = ibegin; it != iend; it++) {
00944         Particle &m = (*it);
00945 
00946         m.age += dt;
00947         m.pos += m.vel * dt;
00948         m.up += m.rvel * dt;
00949     }
00950 }
00951 
00952 // Accelerate particles towards a line
00953 void PAOrbitLine::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
00954 {
00955     float magdt = magnitude * dt;
00956     float max_radiusSqr = fsqr(max_radius);
00957 
00958     if(max_radiusSqr < P_MAXFLOAT) {
00959         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00960             Particle &m = (*it);
00961 
00962             // Figure direction to particle from base of line.
00963             pVec f = m.pos - p;
00964 
00965             // Projection of particle onto line
00966             pVec w = axis * (f * axis);
00967 
00968             // Direction from particle to nearest point on line.
00969             pVec into = w - f;
00970 
00971             // Distance to line (force drops as 1/r^2, normalize by 1/r)
00972             // Soften by epsilon to avoid tight encounters to infinity
00973             float rSqr = into.length2();
00974 
00975             if(rSqr < max_radiusSqr)
00976                 // Step velocity with acceleration
00977                 m.vel += into * (magdt / (sqrtf(rSqr) * (rSqr + epsilon)));
00978         }
00979     } else {
00980         // If not using radius cutoff, avoid the if().
00981         for (ParticleList::iterator it = ibegin; it != iend; it++) {
00982             Particle &m = (*it);
00983 
00984             // Figure direction to particle from base of line.
00985             pVec f = m.pos - p;
00986 
00987             // Projection of particle onto line
00988             pVec w = axis * (f * axis);
00989 
00990             // Direction from particle to nearest point on line.
00991             pVec into = w - f;
00992 
00993             // Distance to line (force drops as 1/r^2, normalize by 1/r)
00994             // Soften by epsilon to avoid tight encounters to infinity
00995             float rSqr = into.length2();
00996 
00997             // Step velocity with acceleration
00998             m.vel += into * (magdt / (sqrtf(rSqr) * (rSqr + epsilon)));
00999         }
01000     }
01001 }
01002 
01003 // Accelerate particles towards a point
01004 void PAOrbitPoint::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01005 {
01006     float magdt = magnitude * dt;
01007     float max_radiusSqr = max_radius * max_radius;
01008 
01009     if(max_radiusSqr < P_MAXFLOAT) {
01010         for (ParticleList::iterator it = ibegin; it != iend; it++) {
01011             Particle &m = (*it);
01012 
01013             // Figure direction to particle.
01014             pVec dir(center - m.pos);
01015 
01016             // Distance to gravity well (force drops as 1/r^2, normalize by 1/r)
01017             // Soften by epsilon to avoid tight encounters to infinity
01018             float rSqr = dir.length2();
01019 
01020             // Step velocity with acceleration
01021             if(rSqr < max_radiusSqr)
01022                 m.vel += dir * (magdt / (sqrtf(rSqr) * (rSqr + epsilon)));
01023         }
01024     } else {
01025         // If not using radius cutoff, avoid the if().
01026         for (ParticleList::iterator it = ibegin; it != iend; it++) {
01027             Particle &m = (*it);
01028 
01029             // Figure direction to particle.
01030             pVec dir(center - m.pos);
01031 
01032             // Distance to gravity well (force drops as 1/r^2, normalize by 1/r)
01033             // Soften by epsilon to avoid tight encounters to infinity
01034             float rSqr = dir.length2();
01035 
01036             // Step velocity with acceleration
01037             m.vel += dir * (magdt / (sqrtf(rSqr) * (rSqr + epsilon)));
01038         }
01039     }
01040 }
01041 
01042 // Accelerate in random direction each time step
01043 void PARandomAccel::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01044 {
01045     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01046         Particle &m = (*it);
01047 
01048         pVec accel = gen_acc->Generate();
01049 
01050         // dt will affect this by making a higher probability of
01051         // being near the original velocity after unit time. Smaller
01052         // dt approach a normal distribution instead of a square wave.
01053         m.vel += accel * dt;
01054     }
01055 }
01056 
01057 // Immediately displace position randomly
01058 void PARandomDisplace::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01059 {
01060     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01061         Particle &m = (*it);
01062 
01063         pVec disp = gen_disp->Generate();
01064 
01065         // dt will affect this by making a higher probability of
01066         // being near the original position after unit time. Smaller
01067         // dt approach a normal distribution instead of a square wave.
01068         m.pos += disp * dt;
01069     }
01070 }
01071 
01072 // Immediately assign a random velocity
01073 void PARandomVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01074 {
01075     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01076         Particle &m = (*it);
01077 
01078         pVec velocity = gen_vel->Generate();
01079 
01080         // Shouldn't multiply by dt because velocities are
01081         // invariant of dt. How should dt affect this?
01082         m.vel = velocity;
01083     }
01084 }
01085 
01086 // Immediately assign a random rotational velocity
01087 void PARandomRotVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01088 {
01089     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01090         Particle &m = (*it);
01091 
01092         pVec velocity = gen_vel->Generate();
01093 
01094         // Shouldn't multiply by dt because velocities are
01095         // invariant of dt. How should dt affect this?
01096         m.rvel = velocity;
01097     }
01098 }
01099 
01100 #if 0
01101 // Produce coefficients of a velocity function v(t)=at^2 + bt + c
01102 // satisfying initial x(0)=x0,v(0)=v0 and desired x(t)=x1,v(t)=v1,
01103 // where x = x(0) + integral(v(T),0,t)
01104 static inline void _pconstrain(float x0, float v0, float x1, float v1,
01105                                float t, float *a, float *b, float *c)
01106 {
01107     *c = v0;
01108     *b = 2 * (-t*v1 - 2*t*v0 + 3*x1 - 3*x0) / (t * t);
01109     *a = 3 * (t*v1 + t*v0 - 2*x1 + 2*x0) / (t * t * t);
01110 }
01111 
01112 // Solve for a desired-behavior velocity function in each axis
01113 // _pconstrain(m.pos.x(), m.vel.x(), m.posB.x(), 0., timeLeft, &a, &b, &c);
01114 
01115 // Figure new velocity at next timestep
01116 // m.vel.x() = a * dtSqr + b * dt + c;
01117 #endif
01118 
01119 // Figure new velocity at next timestep
01120 static inline void Restore(pVec &vel, const pVec &posB, const pVec &pos, const float t,
01121                            const float dtSqr, const float ttInv6dt, const float tttInv3dtSqr)
01122 {
01123     pVec b = (vel*-0.6667f*t + posB - pos) * ttInv6dt;
01124     pVec a = (vel*t - posB - posB + pos + pos) * tttInv3dtSqr;
01125     vel += a + b;
01126 }
01127 
01128 // Over time, restore particles to initial positions
01129 void PARestore::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01130 {
01131     if(time_left <= 0) {
01132         for (ParticleList::iterator it = ibegin; it != iend; it++) {
01133             Particle &m = (*it);
01134 
01135             // Already constrained; keep it there.
01136             m.pos = m.posB;
01137             m.vel = pVec(0.0f,0.0f,0.0f);
01138             m.rvel = pVec(0.0f,0.0f,0.0f);
01139             m.up = m.upB;
01140         }
01141     } else {
01142         float t = time_left;
01143         float dtSqr = fsqr(dt);
01144         float ttInv6dt = dt * 6.0f / fsqr(t);
01145         float tttInv3dtSqr = dtSqr * 3.0f / (t * t * t);
01146 
01147         for (ParticleList::iterator it = ibegin; it != iend; it++) {
01148             Particle &m = (*it);
01149 
01150             if (restore_velocity)
01151                 Restore(m.vel, m.posB, m.pos, t, dtSqr, ttInv6dt, tttInv3dtSqr);
01152             if (restore_rvelocity)
01153                 Restore(m.rvel, m.upB, m.up, t, dtSqr, ttInv6dt, tttInv3dtSqr);
01154         }
01155     }
01156 
01157     time_left -= dt;
01158 }
01159 
01160 // Kill particles with positions on wrong side of the specified domain
01161 void PASink::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01162 {
01163     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
01164 
01165     // Must traverse list in carefully so Remove will work
01166     for (ParticleList::iterator it = ibegin; it != group.end(); ) {
01167         Particle &m = (*it);
01168 
01169         // Remove if inside/outside flag matches object's flag
01170         if(!(position->Within(m.pos) ^ kill_inside))
01171             group.Remove(it);
01172         else
01173             it++;
01174     }
01175 }
01176 
01177 // Kill particles with velocities on wrong side of the specified domain
01178 void PASinkVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01179 {
01180     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
01181 
01182     // Must traverse list carefully so Remove will work
01183     for (ParticleList::iterator it = ibegin; it != group.end(); ) {
01184         Particle &m = (*it);
01185 
01186         // Remove if inside/outside flag matches object's flag
01187         if(!(velocity->Within(m.vel) ^ kill_inside))
01188             group.Remove(it);
01189         else
01190             it++;
01191     }
01192 }
01193 
01194 // Sort the particles by their projection onto the Look vector
01195 void PASort::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01196 {
01197     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
01198 
01199     // First compute projection of particle onto view vector
01200     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01201         Particle &m = (*it);
01202         pVec ToP = m.pos - Eye;
01203         m.tmp0 = ToP * Look;
01204     }
01205 
01206     // sort<ParticleList::iterator>(ibegin, iend);
01207     std::sort<Particle *>(&*ibegin, &*iend);
01208 }
01209 
01210 // Randomly add particles to the system
01211 void PASource::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01212 {
01213     PASSERT(ibegin == group.begin() && iend == group.end(), "Can only be done on whole list");
01214 
01215     size_t rate = size_t(floor(particle_rate * dt));
01216 
01217     // Dither the fractional particle in time.
01218     if(pRandf() < particle_rate * dt - float(rate))
01219         rate++;
01220 
01221     // Don't emit more than it can hold.
01222     if(group.size() + rate > group.GetMaxParticles())
01223         rate = group.GetMaxParticles() - group.size();
01224 
01225     if(vertexB_tracks) {
01226         for(size_t i = 0; i < rate; i++) {
01227             pVec pos, up, vel, rvel, siz, col, al;
01228 
01229             pos = position->Generate();
01230             up = upVec->Generate();
01231             vel = velocity->Generate();
01232             rvel = rvelocity->Generate();
01233             siz = size->Generate();
01234             col = color->Generate();
01235             al = alpha->Generate();
01236             float ag = age + pNRandf(age_sigma);
01237 
01238             group.Add(pos, pos, up, vel, rvel, siz, col, al.x(), ag);
01239         }
01240     } else {
01241         for(size_t i = 0; i < rate; i++) {
01242             pVec pos, posB, up, vel, rvel, siz, col, al;
01243 
01244             pos = position->Generate();
01245             posB = positionB->Generate();
01246             up = upVec->Generate();
01247             vel = velocity->Generate();
01248             rvel = rvelocity->Generate();
01249             siz = size->Generate();
01250             col = color->Generate();
01251             al = alpha->Generate();
01252             float ag = age + pNRandf(age_sigma);
01253 
01254             group.Add(pos, posB, up, vel, rvel, siz, col, al.x(), ag);
01255         }
01256     }
01257 }
01258 
01259 void PASpeedLimit::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01260 {
01261     float min_sqr = fsqr(min_speed);
01262     float max_sqr = fsqr(max_speed);
01263 
01264     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01265         Particle &m = (*it);
01266         float sSqr = m.vel.length2();
01267         if(sSqr<min_sqr && sSqr) {
01268             float s = sqrtf(sSqr);
01269             m.vel *= (min_speed/s);
01270         } else if(sSqr>max_sqr) {
01271             float s = sqrtf(sSqr);
01272             m.vel *= (max_speed/s);
01273         }
01274     }
01275 }
01276 
01277 // Change color of all particles toward the specified color
01278 void PATargetColor::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01279 {
01280     float scaleFac = scale * dt;
01281 
01282     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01283         Particle &m = (*it);
01284         m.color += (color - m.color) * scaleFac;
01285         m.alpha += (alpha - m.alpha) * scaleFac;
01286     }
01287 }
01288 
01289 // Change sizes of all particles toward the specified size
01290 void PATargetSize::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01291 {
01292     pVec scaleFac = scale * dt;
01293 
01294     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01295         Particle &m = (*it);
01296         pVec dif = size - m.size;
01297         m.size += CompMult(dif, scaleFac);
01298     }
01299 }
01300 
01301 // Change velocity of all particles toward the specified velocity
01302 void PATargetVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01303 {
01304     float scaleFac = scale * dt;
01305 
01306     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01307         Particle &m = (*it);
01308         m.vel += (velocity - m.vel) * scaleFac;
01309     }
01310 }
01311 
01312 // Change velocity of all particles toward the specified velocity
01313 void PATargetRotVelocity::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01314 {
01315     float scaleFac = scale * dt;
01316 
01317     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01318         Particle &m = (*it);
01319         m.rvel += (velocity - m.rvel) * scaleFac;
01320     }
01321 }
01322 
01323 // magnitude is how much acceleration to apply at the top of the vortex (non-tip end)
01324 // tightnessExponent is like a phong exponent that gives a curve to the vortex silhouette
01325 // axis is the vector along the center of the vortex starting at p
01326 // p is the tip of the vortex
01327 void PAVortex::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01328 {
01329     float magdt = magnitude * dt;
01330     float max_radiusSqr = fsqr(max_radius);
01331     float axisLength = axis.length();
01332     float axisLengthInv = 1.0f / axisLength;
01333     pVec axisN = axis;
01334     axisN.normalize();
01335 
01336     // This one just rotates a particle around the axis. Amount is based on radius, magnitude, and mass.
01337     for (ParticleList::iterator it = ibegin; it != iend; it++) {
01338         Particle &m = (*it);
01339 
01340         // Figure direction to particle from base of line.
01341         pVec tipToPar = m.pos - tip;
01342 
01343         // Projection of particle onto line
01344         float axisScale = tipToPar * axisN;
01345         pVec parOnAxis = axisN * axisScale;
01346 
01347         // Direction from particle to nearest point on line.
01348         pVec parToAxis = parOnAxis - tipToPar;
01349 
01350         // Distance to axis
01351         float rSqr = parToAxis.length2();
01352         float alongAxis = axisScale * axisLengthInv;
01353 
01354         // This is how much to scale the vortex's force by as a function of how far up the axis the particle is.
01355         float alongAxisPow = powf(alongAxis, tightnessExponent);
01356         float silhouetteSqr = fsqr(alongAxisPow * max_radius);
01357 
01358         if(rSqr >= max_radiusSqr || axisScale < 0.0f || alongAxis > 1.0f) {
01359             // m.color = pVec(0,0,1);
01360             continue;
01361         }
01362         pVec AccelUp = axisN * 0.011f * dt; // XXX Make this a param.
01363         m.vel += AccelUp;
01364 
01365         if(rSqr >= silhouetteSqr) {
01366             // m.color = pVec(1,0,0);
01367             continue;
01368         }
01369 
01370         // m.color = pVec(0,1,0);
01371         AccelUp = axisN * -0.001f * dt; // XXX Make this a param.
01372         m.vel += AccelUp;
01373 
01374         // Apply tightness
01375         float r = sqrtf(rSqr);
01376 
01377         // Accelerate toward axis. Force drops as 1/r^2, normalize by 1/r.
01378         // Soften by epsilon to avoid tight encounters to infinity
01379         pVec AccelIn = parToAxis * ((1.0f - alongAxisPow) * magdt / (m.mass * (r * (rSqr + epsilon))));
01380         m.vel += AccelIn;
01381 
01382         // Accelerate around axis by constructing orthogonal vector frame of axis, parToAxis, and RotAccel.
01383         pVec RotAccel = Cross(axisN, parToAxis);
01384         float RA = RotAccel.length();
01385         float scale = rotSpeed / RA;
01386         RotAccel *= scale;
01387 
01388         pVec dst = RotAccel - parToAxis;
01389         float DA = dst.length();
01390         dst *= (r / DA);
01391         pVec travel = dst + parToAxis;
01392 
01393         pVec AccelAround = travel * (dt / m.mass);
01394         m.vel += AccelAround;
01395     }
01396 }
01397 
01398 // This is an experimental Action.
01399 // It's mostly for the purpose of seeing how big the speedup can be
01400 // if we apply all actions to a particle at once,
01401 // rather than doing an action to all particles at once.
01402 void PAFountain::Execute(ParticleGroup &group, ParticleList::iterator ibegin, ParticleList::iterator iend)
01403 {
01404 #if 0
01405     assert(ibegin == group.begin() && iend == group.end()); // Can only be done on whole list.
01406 
01407     //pGravity(0.0, 0.0, -0.01);
01408     //pSinkVelocity(true, PDSphere, 0, 0, 0, 0.01);
01409     //pBounce(-0.05, 0.35, 0, PDDisc, 0, 0, 0,  0, 0, 1,  5);
01410     //pSink(false, PDPlane, 0,0,-3, 0,0,1);
01411     //pMove();
01412 
01413     // For pGravity
01414     PAGravity *PGr = (PAGravity *)(AL+0);
01415     pVec ddir(PGr->direction * dt);
01416 
01417     PASinkVelocity *PSV = (PASinkVelocity *)(AL+1);
01418 
01419     // For bounce disc
01420     PABounce *PB = (PABounce *)(AL+2);
01421     float r1Sqr = fsqr(PB->position.radius1);
01422     float r2Sqr = fsqr(PB->position.radius2);
01423 
01424     // For sink
01425     PASink*PS = (PASink*)(AL+3);
01426 
01427     // Must traverse list carefully so Remove will work
01428     for (ParticleList::iterator it = ibegin; it != group.end(); ) {
01429         Particle &m = (*it);
01430 
01431         //pGravity(0.0, 0.0, -0.01);
01432         // Step velocity with acceleration
01433         m.vel += ddir;
01434 
01435         //pSinkVelocity(true, PDSphere, 0, 0, 0, 0.01);
01436         // Remove if inside/outside flag matches object's flag
01437         if(!(PSV->velocity.Within(m.vel) ^ PSV->kill_inside)) {
01438             group.Remove(it);
01439             continue;
01440         }
01441 
01442         //pBounce(-0.05, 0.35, 0, PDDisc, 0, 0, 0,  0, 0, 1,  5);
01443 
01444         // See if particle's current and next positions cross plane.
01445         // If not, couldn't bounce, so keep going.
01446         pVec pnext(m.pos + m.vel * dt);
01447 
01448         // nrm stores the plane normal (the a,b,c of the plane eqn).
01449         // Old and new distances: dist(p,plane) = n * p + d
01450         // radius1 stores -n*p, which is d. radius1Sqr stores d.
01451         float distold = m.pos * PB->position.nrm + PB->position.radius1Sqr;
01452         float distnew = pnext * PB->position.nrm + PB->position.radius1Sqr;
01453 
01454         if(!pSameSign(distold, distnew)) {
01455             // Find position at the crossing point by parameterizing
01456             // p(t) = pos + vel * t
01457             // Solve dist(p(t),plane) = 0 e.g.
01458             // n * p(t) + D = 0 ->
01459             // n * p + t (n * v) + D = 0 ->
01460             // t = -(n * p + D) / (n * v)
01461             // Could factor n*v into distnew = distold + n*v and save a bit.
01462             // Safe since n*v != 0 assured by quick rejection test.
01463             // This calc is indep. of dt because we have established that it
01464             // will hit before dt. We just want to know when.
01465             float nv = PB->position.nrm * m.vel;
01466             float t = -distold / nv;
01467 
01468             // Actual intersection point p(t) = pos + vel t
01469             pVec phit(m.pos + m.vel * t);
01470 
01471             // Offset from origin in plane, phit - origin
01472             pVec offset(phit - PB->position.p1);
01473 
01474             float rad = offset.length2();
01475 
01476             if(!(rad > r1Sqr || rad < r2Sqr)) {
01477                 // A hit! A most palpable hit!
01478 
01479                 // Compute tangential and normal components of velocity
01480                 pVec vn(PB->position.nrm * nv); // Normal Vn = (V.N)N
01481                 pVec vt(m.vel - vn); // Tangent Vt = V - Vn
01482 
01483                 // Compute new velocity heading out:
01484                 // Don't apply friction if tangential velocity < cutoff
01485                 if(vt.length2() <= PB->cutoffSqr)
01486                     m.vel = vt - vn * PB->resilience;
01487                 else
01488                     m.vel = vt * PB->oneMinusFriction - vn * PB->resilience;
01489             }
01490         }
01491 
01492         //pSink(false, PDPlane, 0,0,-3, 0,0,1);
01493         // Remove if inside/outside flag matches object's flag
01494         if(!(PS->position.Within(m.pos) ^ PS->kill_inside)) {
01495             group.Remove(it);
01496             continue;
01497         } else
01498             it++;
01499 
01500         //pMove();
01501         m.age += dt;
01502         m.pos += m.vel * dt;
01503     }
01504 #endif
01505 }

Generated on Sat Mar 15 22:55:58 2008 for Armagetron Advanced by  doxygen 1.5.4