User:Andrew/ProposedLlPushObjectLinear

From Second Life Wiki
Jump to navigation Jump to search

After reading the llPushObject() implementation Seifert Surface and Lex Neva submitted this proposal for a llPushObjectLinear() call that would provide more sane and predictable results. This is NOT implemented, or even officially in the schedule, but I've posted it here as reference when talking about how llPushObject() cannot be fixed and what alternatives exist for going forward. -- Andrew Linden

vector llPushObjectLinear(          //returns the linear impulse actually imparted
        const LLObject& pusher,
        const LLObject& pushee,
        const LLVector3& linear_impulse,
        //const LLVector3& angular_impulse,   //split linear from angular impulse?
         bool use_pushee_local_frame )
{
    LLPhysicsBody& pushee_body = pushee.getPhysicsBody();
    if (!pushee_body || pushee_body.isKeyframed() )
    {
        // some objects don't have collidable bodies (trees, grass)
        // and keyframed (non-dynamic) objects can't move
        return ZERO_VECTOR;
    }
    if ( pushee.isAvatar()
        && pushee.isGod()
        && pusher.getOwnerID() != pushee.getID() )
    {
        // avatars in admin mode cannot be pushed
        // unless they are the pusher's owner
        return ZERO_VECTOR;
    }
    // we have a max dist beyond which push will do nothing, we also have a
    // distance at which the energy use of a push increases beyond (the
    // attenuation distance). When the energy runs out, the push is scaled down
    // appropriately
    F32 PUSH_ATTENUATION_DISTANCE = 20.f;
    F32 PUSH_MAXIMUM_DISTANCE = 40.f;
    F32 distance = (pushee.getPosition() - pusher.getPosition()).length();
    // later we might divide by something very close to zero, -0.001 gives us
    // some leeway for safety
    if(distance >= PUSH_MAXIMUM_DISTANCE - 0.001f)
    {
        // you still lose all your energy if you try to push something too far
        // away.
        pusher.consumeEnergy(1.0);
        return ZERO_VECTOR;
    }
    F32 pusher_mass = pusher.getMass();
    // linear
    LLVector3 applied_linear_impulse = linear_impulse;
    {
        // consume energy according to the intended impulses
        F32 impulse_length = linear_impulse.length();

        F32 desired_energy = impulse_length * max(1.0, (PUSH_MAXIMUM_DISTANCE - PUSH_ATTENUATION_DISTANCE)/(PUSH_MAXIMUM_DISTANCE - distance) ) * SCRIPT_ENERGY_PER_MOMENTUM;
        // the energy required is just the impulse size up until we reach
        // PUSH_ATTENUATION_DISTANCE, at which point the energy starts
        // increasing, blowing up to infinity at PUSH_MAXIMUM_DISTANCE.

        // question: does applyImpulse really do impulse, or is it actually
        // doing a change in velocity? If the latter, then there should be a
        // pushee_mass term in the above.  if the former, then the value of
        // SCRIPT_ENERGY_PER_MOMENTUM may need to change to get sensible energy
        // behaviour, as the old llPushObject had the pusher mass as a factor as
        // well.

        // LLObject::consumeEnergy() returns 1 if there is enough energy, else
        // returns the fraction of energy available.
         F32 scaling_factor = pusher.consumeEnergy(desired_energy);

        // this should kill off any stupidly large pushes, as well as any pushes
        // that are a long way away
        applied_linear_impulse *= scaling_factor;
    }
    if ( use_pushee_local_frame )
    {
        // rotate the directions of the impulses into the pushee's local frame
        LLQuaternion rotation = pushee.getRotation();
        applied_linear_impulse *= rotation;
        applied_angular_impulse *= rotation;
    }
    if (pushee.isAvatar())
    {
        // NOTE: we ignore the angular part since the avatar cannot yet be spun
        // from external or attachment forces
        if (pusher.isAttachment()
            && pusher.getAvatarID() == pushee.getID())
        {
            // this is an attachment pushing its avatar, so the push must go
            // through the avatar controller which will guard against too fast
            // velocities and do the right thing for the current movement mode
            pushee.getAvatarController().accumulateAttachmentImpulse(applied_linear_impulse);
            return applied_linear_impulse;
        }
        else
        {
            // this is an attachment pushing another avatar, so the push must
            // go through the avatar controller which will gaurd against too
            // fast velocities and do the right thing for current movement mode
            pushee.getAvatarController().applyExternalImpulse(applied_linear_impulse);
            return applied_linear_impulse;
        }
    }
    else
    {
        pushee_body.applyImpulse(applied_linear_impulse);
        return applied_linear_impulse;
    }
}