Game Development Community

Changing Collision Poly

by James Randall · in Torque Game Builder · 02/26/2006 (8:05 am) · 16 replies

Hi all,

I've got a plume off steam in the game I'm working on that turns on and off based on a trigger and/or a timer.

Its implemented as a particle effect and looks pretty nice (thanks Mr. Particle Engine). It starts out as a narrow jet and billows up to a cloud at the top when turned on (with the jet still running).

When turned off the jet at the bottom turns off and the billow cloud at the top stays around a little longer and eventually dissipates.

The player needs to dodge the plume of steam and I'd like the collision with the steam to be as accurate as possible.

I'm looking for the best way to implement a reasonably accurate collision.

I've not tried it yet but it seems to me that the best way would be to alter the polygon responsible for detecting the collision over time. So as the steam starts set a schedule going that moves the collision polygon through a set of predetermined shapes ending in a static shape for the steam being on. Then doing the same when the steam turns off.

Just wandering if anyone has a better way as I'm a little worried that on machines of different speeds I could end up with the collision poly getting out of sync with the particle volume I have predicted.

All ideas very gratefully welcome!!

Thanks

James

#1
02/26/2006 (11:48 am)
IIRC (Melv, back me up here), particles themselves can now collide with sceneObjects. I don't think there is much documentation on it available yet however. If you are comfortable in the source code, you may want to take a look.
#2
02/26/2006 (2:23 pm)
I've had a brief look at the code and came across the setEffectCollisionStatus stuff which I've also found mentioned in the pdf reference. Having problems getting it to work though so if anyone can help that would be great.

I'll spend more time going through the code tomorrow but as this is the first time I've ventured in there it will probably take me a while to figure out. Reads easily enough though :)
#3
02/27/2006 (12:08 am)
It's pretty easy to setup, in-fact, it's just an extension of what you already know.

As the reference-doco entry mentions, collisions work identical to standard collisions but when you set the effect collision status to "false", the particles collide, not the effect-object. This status essentially means whether to process collisions using the effect-object (true) or the particles (false).

Set it up so that your effect-object collides using the normal mechanisms with the object(s) in question. When you've done this, set the collision-status to "false" and the particles will collide, not the effect-object.

There are certain "restrictions" though when using particle collisions...

- The only collision-modes supported are "bounce", "clamp", "sticky" and "kill".
- Particles do not affect the destination object.
- There are no collision script callbacks.
- The collisions are point-based.
- The main-object cannot react to collisions whilst particle-collisions are active.
- Particle Collisions can eat performance for breakfast if not used sparingly.

Other than that, they kick ass. ;)

- Melv.
#4
02/27/2006 (10:09 am)
Thanks for the reply. It looks like I've set it up correctly but I've been asuuming I've got it wrong because my callback hadn't been called. I hadn't realised they didn't generate a callback but it makes sense given the other constraints. Hmmm.

Thinking about keeping it optimal I guess what I want to do is this:

1) Is there a collision with the effect object?
2) Yes? Is there a collision with any particle of the effect? (so the minute I find a collision I can stop looking)
3) Yes? Then have the effect object generate a callback.

I've only had Torque a week but started looking at the code last night and it all looks reasonable. I sense my first attempt at an engine mod coming on.....
#5
02/27/2006 (10:54 am)
James,

EDIT: Removed original comment because it sounded a little abrupt; didn't mean to sounds that way! :)

It is possible to institute a callback if any collision was actioned during the frame, but it wouldn't be a callback with any collision information, just the fact that it happened. The reason I didn't do this was because the seperate callback from the standard would confused most people. That, and the fact that as soon as I put a callback in, I know someone would want a list of collisions, which could number in the hundreds!

Essentially though, it is quite simple to implement what you described but it would have to be a "special" callback with minimal information, if any at all.

- Melv.
#6
02/27/2006 (11:06 am)
I didn't see your post pre-edit :).

(Edit of my own: hope you didn't think I was being sarcastic when I was talking about modding the engine. I'm really impressed by the engine itself and the support it gets on these forums both by its designers / coders and other users.)

What you describe in terms of the alternative callback is actually all I need. Literally all I need to know is that a collision occurred between the particle collision group and another collision group. I see exactly where you're coming from on the list of collisions - definetly not practical if you want to keep any form of framerate up. I was actually surprised to find particle collisions had been implemented at all.

Any tips to set me off on the right direction?
#7
03/01/2006 (1:22 am)
Here's a way to do what you want (and please note that this has not been tested, simply typed-up here). Hell, I've not even compiled it!

In "t2dParticleEmitter.h", replace "actionParticleCollisions()" declaration with...
bool actionParticleCollisions( const t2dParticleEffect* pParentEffect, const F32 elapsedTime, t2dPhysics::cCollisionStatus& sendCollisionStatus, CDebugStats* pDebugStats );

In "t2dParticleEmitter.cc", replace "actionParticleCollisions()" implementation with...
bool t2dParticleEmitter::actionParticleCollisions( const t2dParticleEffect* pParentEffect, const F32 elapsedTime, t2dPhysics::cCollisionStatus& sendCollisionStatus, CDebugStats* pDebugStats )
{
    // Reset Initial Collision Status.
    bool collisionStatus = false;

    // Fetch SceneGraph.
    t2dSceneGraph* pSceneGraph = pParentEffect->getSceneGraph();

    // Get Parent Collision Masks.
    const U32 groupMask = pParentEffect->getCollisionGroupMask();
    const U32 layerMask = pParentEffect->getCollisionLayerMask();

    // Get Parent Collision Response.
    const t2dPhysics::eCollisionResponse collisionResponse = pParentEffect->getCollisionResponseMode();

    // Get Parent Restitution.
    const F32 restitution = pParentEffect->getRestitution();

    // Fetch First Particle Node.
    tParticleNode* pParticleNode = mParticleNodeHead.mNextNode;

    // Next Particle Node.
    tParticleNode* pNextParticleNode;

    // Process All particle nodes.
    while ( pParticleNode != &mParticleNodeHead )
    {
        t2dVector newPosition;

        // Default to not suppressing movement.
        pParticleNode->mSuppressMovement = false;

        // Fetch Start Position.
        t2dVector& startPosition = pParticleNode->mPosition;
        // Calculate Projected Position.
        const t2dVector endPosition = pParticleNode->mPosition + (pParticleNode->mVelocity * pParticleNode->mRenderSpeed * elapsedTime);

        // Pick Objects along our velocity path.
        const U32 pickedObjects = pSceneGraph->pickLine( startPosition, endPosition, groupMask, layerMask, false, pParentEffect );

        // Reference next Particle Node.
        // NOTE:-   We do this here because we may destroy the particle if the collision-response is in "KILL" mode.
        pNextParticleNode = pParticleNode->mNextNode;

        // Did we collide?
        if ( pickedObjects > 0 )
        {
            // Flag Collision Occurred.
            collisionStatus = true;

            // Fetch Colliding Object.
            typeSceneObjectVectorConstRef pickVector = pSceneGraph->getPickList();
            // Fetch Scene Object.
            t2dSceneObject* pSceneObject = pickVector[0];
            // Fetch Collisoin Time.
            const F32& collisionTime = pSceneObject->mSortKeyCollisionTime;

            // Lerp to collision position.
            startPosition.lerp( endPosition, collisionTime, startPosition );

            // Reference Velocity.
            t2dVector& linearVelocity = pParticleNode->mVelocity;
            // Reference Collision Normal.
            t2dVector& collisionNormal = pSceneObject->mSortKeyCollisionNormal;

            switch( collisionResponse )
            {
                // Bounce.
                case t2dPhysics::T2D_RESPONSE_BOUNCE:
                {
                    // Calculate Dot Velocity Normal.
                    const F32 dotVelocityNormal = collisionNormal * linearVelocity;
                    // Any velocity to clamp?
                    if ( dotVelocityNormal < 0.0f )
                    {
                        // Set new Linear Velocity.
                        linearVelocity -= (collisionNormal*(2.0f*dotVelocityNormal) * restitution);
                    }

                } break;

                // Clamp.
                case t2dPhysics::T2D_RESPONSE_CLAMP:
                {
                    // Calculate Dot Velocity Normal.
                    const F32 dotVelocityNormal = collisionNormal * linearVelocity;
                    // Any velocity to clamp?
                    if ( dotVelocityNormal < 0.0f )
                    {
                        // Set new Linear Velocity.
                        linearVelocity -= collisionNormal*dotVelocityNormal;
                    }

                } break;

                // Bounce.
                case t2dPhysics::T2D_RESPONSE_STICKY:
                {
                    // Set at rest.
                    linearVelocity.set(0.0f,0.0f);

                } break;

                // Kill.
                case t2dPhysics::T2D_RESPONSE_KILL:
                {
                    // Free Particle.
                    freeParticle( pParticleNode );

                } break;
            };

            // Suppress Movment.
            pParticleNode->mSuppressMovement = true;
        }

        // Move to next Particle Node.
        pParticleNode = pNextParticleNode;
    };   

    // Return Collision Status.
    return collisionStatus;
}


In "t2dParticleEffect.cc", replace "actionParticleCollisions()" implementation with...
bool t2dParticleEffect::checkCollisionSend( const F32 elapsedTime, t2dPhysics::cCollisionStatus& sendCollisionStatus, CDebugStats* pDebugStats )
{
    // Use Effect Collisions?
    if ( mUseEffectCollisions )
    {
        // Yes, so use standard core-collision.
        return Parent::checkCollisionSend( elapsedTime, sendCollisionStatus, pDebugStats );
    }


// T2D Debug Profiling.
#ifdef T2D_DEBUG_PROFILING
    PROFILE_START(T2D_t2dParticleEffect_checkCollisionSend);
#endif

    // Reset Initial Collision Status.
    bool collisionStatus = false;

    // Update all emitters.
    for ( U32 n = 0; n < mParticleEmitterList.size(); n++ )
    {
        // Fetch Particle Emitter Reference.
        t2dParticleEmitter*& particleEmitter = mParticleEmitterList[n];

        // Integrate Emitter (if visible).
        if ( particleEmitter->getVisible() && particleEmitter->getEmitterCollisionStatus() )
        {
            // Action Particle Collision.
            if ( particleEmitter->actionParticleCollisions( this, elapsedTime, sendCollisionStatus, pDebugStats ) )
            {
                // Flag Collision Occurred.
                collisionStatus = true;
            }
        }
    }

    // Did a collision occur.
    if ( collisionStatus )
    {
        // Yes, so perform callback.
        Con::executef( this, 1, "onParticleCollision" );
    }

// T2D Debug Profiling.
#ifdef T2D_DEBUG_PROFILING
    PROFILE_END();   // T2D_t2dParticleEffect_checkCollisionSend
#endif

    // Return No Collision Always.
    // NOTE:-   This stops the particle-effect object from ever receiving collisions itself!
    return false;
}


You should now get, a script-callback "onParticleCollision(%this)" if any particle of any sub-emitter of the effect collides with anything. Note, as described above, you'll only get a single callback for all particle collisions for each effect.

I'll definatley add something similar to the codebase but if you want to be ultra future-proof, you'll more than likely find that I'll change the function from "actionParticleCollisions()" to the more consistently named "checkParticleCollisions()".

Hope this helps,

- Melv.
#8
03/01/2006 (10:12 am)
You, sir, are a star!!

I will give this a go as soon as possible.

If I ever see you at a Torque Boot Camp I'll buy you a beer :)
#9
03/01/2006 (11:33 am)
No problem at all.

BTW, if you're going to the one next week, I'll be there. :)

- Melv.
#10
03/01/2006 (12:43 pm)
Sadly not. Though if I'd discovered Torque six months ago I may well have made the trip.

Still there's always next time :)
#11
03/07/2006 (10:15 am)
Just been trying this out. It doesn't seem to quite work. Though from tracing it through it very nearly does. I'm wandering if there's a slight change needed elsewhere as t2dSceneContainer::pickLine doesn't seem to succeed. It passes all the basic tests for a collision between my object and the particle and gets right through to performing the line-cast here:

if ( pSceneObjectRef->getParentPhysics().castLine( lineStart, lineEnd, pSceneObjectRef->mSortKeyCollisionNormal, pSceneObjectRef->mSortKeyCollisionTime ) )

Which on my build is line 719 of t2dSceneContainer.cc. This never equates to true. Given it gets this far (and I have traced it through the new code and played around a little) it seems I've got everything set up correctly (group and layer masks, emitter and effect collisions etc.) but this line cast fails for some reason.

I'll attempt to trace it through further myself later.
#12
03/07/2006 (5:53 pm)
I've confirmed that picking is currently broken with the latest build and custom poly lists--Melv has it on his list (I ran into this myself last week).
#13
03/07/2006 (11:12 pm)
Phew. I'm not just being a bonehead then :)

Thanks for letting me know Stephen.
#14
03/12/2006 (2:16 am)
Stephen,

Sorry, I must've missed this one (or forgot)! With all the travelling lately, I'm not suprised. ;)

Anyway, in what way is picking broken? Also, particle collisions are working fine as far as I can tell.

- Melv.
#15
03/12/2006 (1:08 pm)
This is the one I demonstrated to you Melv with a custom poly--if you have a hand drawn custom poly (using the poly editor, or manually editing the collision points) , pickPoint will not return that object as part of a picking list.

Works fine with basic collision poly primitives, just not custom.
#16
03/13/2006 (10:34 am)
Ah okay, I was probably tired and didn't pick up on that (ooh, bad pun), sorry.

I'll get that on Mantis and try to confirm the problem.

Thanks for reminding me! :)

- Melv.