Game Development Community

Improved particle system v2.0.0 is live! Now with particle physics!

by Lukas Joergensen · in Torque 3D Professional · 05/27/2012 (4:15 pm) · 21 replies

Patch video of patch 2.0.0

(Switch to 3 minutes in to just see the new features in 2.0.0)

Highlights:
  • Attractive particles
  • Particle repulsion
  • Particle collision

  • Download it here!
    Page «Previous 1 2
    #1
    05/27/2012 (4:44 pm)
    Wow... nice work :)

    How about making your changes directly to a svn shared repository? So maybe you can follow the bug reporting and more.
    #2
    05/27/2012 (4:50 pm)
    Ooh it's that site I lost all info on.
    But have to ask, what protection does it have? Cuz' I don't believe I am allowed to release these source code files unless only T3D users have access to it :)
    #3
    05/27/2012 (4:55 pm)
    Yes, in fact everything is protected by SSL connections, and access is granted only to users with a valid license. Just ask, and i will verify from your profile if you have the license or not, then i will create an account for you.
    #4
    05/27/2012 (4:58 pm)
    Will create an account then and see if I get it used :P
    #5
    05/27/2012 (5:01 pm)
    Give me please your email to send the info
    #6
    05/27/2012 (5:03 pm)
    --My first name--PJ@clan-net.dk
    #7
    05/27/2012 (11:38 pm)
    This is just beautiful. Where is the donate link? With Paypal you can create a donate link that goes in an email. I don't see any reason why you could not put that at the bottom of your resource.

    Here is an example of one:
    Donate Link

    Alfio, I just sent you a donation. It is not much, but it should help some.

    Also, could you add me to the repo? Thanks.
    #8
    05/28/2012 (12:55 am)
    Hmm, I think I will add a donations button if you are even asking for it :) Just hoping it wont be against any of GarageGames politics.
    #9
    05/29/2012 (4:22 am)
    Put it on a separate webpage then. I saw the other thread so maybe we should wait to see that we don't put GG in a bad position by posting donate links here.
    #10
    05/29/2012 (5:55 am)
    Forgot i had this post! Fixed in a second!
    #11
    05/29/2012 (8:11 am)
    And why hasn't GarageGames hired you yet? :P Awesome work, Lukas! So many possibilities!
    #12
    05/30/2012 (1:13 pm)
    @Jordan I don't know! Ask them! :D
    Actually the changes here are really simple, especially the particle physics. And they aren't really hurting the framerate that much. I just don't know why it wasn't there to begin with! :D
    #13
    06/03/2012 (10:56 am)
    Is it possible for particles to add force to objects? In the future I'll need to be able to make water spray from boats affect other boats.Scott
    #14
    06/03/2012 (10:59 am)
    @Scott, certainly!
    Look in: void GraphEmitter::update( U32 ms ) in GraphEmitter.cpp

    Find this code:
    // added part ----------------
    	RayInfo rInfo;
    	if(gClientContainer.castRay(part->pos, part->pos + part->vel * t, TerrainObjectType | InteriorObjectType | VehicleObjectType | PlayerObjectType | StaticShapeObjectType, &rInfo))
    	{
    rInfo contains the object which the particles collided with! Just kind of add a rInfo.object->applyForce or something.
    I have considered adding a callback in that function but I don't have the time to work on the resource atm.
    Edit i believe you can write something like:
    rInfo.object->applyImpulse(part->pos,part->pos + part->vel * t);
    Maybe tweak the values a bit.
    #15
    06/03/2012 (4:25 pm)
    Hey guys! Seems like I have made another mistake and accidentally overridden the emitParticles function which the projectile class uses!
    Are no one experiencing these errors or are you simply not telling me about them? :P
    To fix paste this in between the emitParticles functions:
    In the particleEmitter.h file:
    void emitParticles(const Point3F& start,
                          const Point3F& end,
                          const Point3F& axis,
                          const Point3F& velocity,
                          const U32      numMilliseconds);
    In the particleEmitter.cpp file:
    #16
    06/03/2012 (4:26 pm)
    //-----------------------------------------------------------------------------
    // emitParticles
    //-----------------------------------------------------------------------------
    void ParticleEmitter::emitParticles(const Point3F& start,
                                        const Point3F& end,
                                        const Point3F& axis,
                                        const Point3F& velocity,
                                        const U32      numMilliseconds)
    {
       if( mDead ) return;
       
       if( mDataBlock->particleDataBlocks.empty() )
          return;
    
       // lifetime over - no more particles
       if( mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS )
       {
          return;
       }
    
       U32 currTime = 0;
       bool particlesAdded = false;
    
       Point3F axisx;
       if( mFabs(axis.z) < 0.9f )
          mCross(axis, Point3F(0, 0, 1), &axisx);
       else
          mCross(axis, Point3F(0, 1, 0), &axisx);
       axisx.normalize();
    
       if( mNextParticleTime != 0 )
       {
          // Need to handle next particle
          //
          if( mNextParticleTime > numMilliseconds )
          {
             // Defer to next update
             //  (Note that this introduces a potential spatial irregularity if the owning
             //   object is accelerating, and updating at a low frequency)
             //
             mNextParticleTime -= numMilliseconds;
             mInternalClock += numMilliseconds;
             mLastPosition = end;
             mHasLastPosition = true;
             return;
          }
          else
          {
             currTime       += mNextParticleTime;
             mInternalClock += mNextParticleTime;
             // Emit particle at curr time
    
             // Create particle at the correct position
             Point3F pos;
             pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
             addParticle(pos, axis, velocity, axisx);
             particlesAdded = true;
             mNextParticleTime = 0;
          }
       }
    
       while( currTime < numMilliseconds )
       {
          S32 nextTime = mDataBlock->ejectionPeriodMS;
          if( mDataBlock->periodVarianceMS != 0 )
          {
             nextTime += S32(gRandGen.randI() % (2 * mDataBlock->periodVarianceMS + 1)) -
                         S32(mDataBlock->periodVarianceMS);
          }
          AssertFatal(nextTime > 0, "Error, next particle ejection time must always be greater than 0");
    
          if( currTime + nextTime > numMilliseconds )
          {
             mNextParticleTime = (currTime + nextTime) - numMilliseconds;
             mInternalClock   += numMilliseconds - currTime;
             AssertFatal(mNextParticleTime > 0, "Error, should not have deferred this particle!");
             break;
          }
    
          currTime       += nextTime;
          mInternalClock += nextTime;
    
          // Create particle at the correct position
          Point3F pos;
          pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
          addParticle(pos, axis, velocity, axisx);
          particlesAdded = true;
    
          //   This override-advance code is restored in order to correctly adjust
          //   animated parameters of particles allocated within the same frame
          //   update. Note that ordering is important and this code correctly 
          //   adds particles in the same newest-to-oldest ordering of the link-list.
          //
          // NOTE: We are assuming that the just added particle is at the head of our
          //  list.  If that changes, so must this...
          U32 advanceMS = numMilliseconds - currTime;
          if (mDataBlock->overrideAdvance == false && advanceMS != 0) 
          {
             Particle* last_part = part_list_head.next;
             if (advanceMS > last_part->totalLifetime) 
             {
               part_list_head.next = last_part->next;
               n_parts--;
               last_part->next = part_freelist;
               part_freelist = last_part;
             } 
             else 
             {
                if (advanceMS != 0)
                {
                  F32 t = F32(advanceMS) / 1000.0;
    
                  Point3F a = last_part->acc;
                  a -= last_part->vel * last_part->dataBlock->dragCoefficient;
                  a -= mWindVelocity * last_part->dataBlock->windCoefficient;
                  a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient;
    
                  last_part->vel += a * t;
                  last_part->pos += last_part->vel * t;
    
                  updateKeyData( last_part );
                }
             }
          }
       }
    
       // DMMFIX: Lame and slow...
       if( particlesAdded == true )
          updateBBox();
    
    
       if( n_parts > 0 && getSceneManager() == NULL )
       {
          gClientSceneGraph->addObjectToScene(this);
          ClientProcessList::get()->addObject(this);
       }
    
       mLastPosition = end;
       mHasLastPosition = true;
    }
    #17
    06/03/2012 (11:58 pm)
    Hey Lukas, I tried what you said for applying the impulse, but it seems to have no effect on physicsShapes. In fact the castRay doesn't seem to work at all. It doesn't find physicsShapes, at least I can't figure out a way to do it, I tried just about every combination of ObjectType flags. Is it not possible?

    Edit:
    I figured out that I needed to consult the PHYSICSMGR for this functionality. However it still seems to have no effect on the physicsShape, it does get into the if statement now though.
    if(PHYSICSMGR->getWorld(isServerObject() ? "server" : "client" )->castRay(part->pos, part->pos + part->vel * t, &rInfo, Point3F(0,0,0))) 
    	{
    		rInfo.object->applyImpulse(part->pos,part->pos + part->vel * t);
    	}

    Scott
    #18
    06/04/2012 (1:44 am)
    @Scott
    Oh so you are using physX?
    Try have a look at this comment then:
    http://www.garagegames.com/community/forums/viewthread/105537/1#comment-728083
    Seems like physicsShape isn't using the same raycast function
    So instead you would do something like this:
    PhysicsWorld *world = gPhysicsPlugin->getWorld( "Client" /*( or "Server" )*/);  
            // see if we are in a physics world  
        if ( world )  
        {  
                    // get the physx hit obj  
            hitP = world->castRay( startPnt, endPnt, &rInfo, Point3F(0,0,0) );  
            if ( hitP )
    I don't know what the last point is for.
    #19
    06/04/2012 (2:02 am)
    Heh, just edited my post as you said that apparantly.

    I'm trying to use bullet, but it doesn't seem to have any effect. I tried it with the first way with the BouncingBoulder Rigid shape, and I couldn't get the particles to push it either.
    #20
    06/04/2012 (2:10 am)
    Hmm you say it goes into the if sentence, can you check that it is colliding with the right object? So it isn't applying the impulse to the particles or something.
    Also try and check if there is any special function for applying an impulse to a physX object, never worked with the physX so I don't know what to take into account.
    Page «Previous 1 2