0
\$\begingroup\$

I am trying to add sleep optimization to my little physics engine(just for learning). I use SAT for collision detection that returns penetration normal and depth without contact points. Objects are put to sleep after 180 frames if their velocity is less then eps. When objects are stacked and in sleeping state when i add velocity to the bottom object, objects on top remain in sleep mode and just float in the air. Can sleeping be implemented without contact points at all? This is done in 3d.

This is collision loop:

void Physics::updateReal(float dt){
    auto& rbs = EntitySystem::instance()->_rigidbodies;

    for (auto rb : rbs) {
        if(rb->isAwake())
            rb->integrate(dt);
    }

    for (int i = 0; i < 1; ++i) { //can be called multiple times per frame to improve stability
        blackList.clear();
        //std::cout << "########" << std::endl;
        for (auto rb : rbs) {
            if (!rb->isAwake() || rb->_inverseMass == 0.0f) continue;       //skip static and sleeping rbs

            //colliders c1 of current rigidbody
            std::vector<Collider*> RBColliders; rb->thisEntity()->getColliders(RBColliders);

            for (auto c1 : RBColliders) {
                glm::vec3 o, d; c1->getAABB(o, d); std::vector<BVHTreeContainee*> colliders2;
                SpatialPartitioning::instance()->queryAABB(colliders2, o, d, BVH_COLLIDERS);

                for (BVHTreeContainee* c2bvh : colliders2) {
                    auto c2 = (Collider*)c2bvh;
                    if (!c2->isRigidbody()) continue;
                    if (c1 == c2) continue;
                    if (c1->getEntity() == c2->getEntity()) continue;

                    //std::cout << c1->getEntity()->gameObject()->name << " AND " << c2->getEntity()->gameObject()->name << std::endl;
                    collide(c1, c2);
                }
                //blackList.push_back(c1);  //c1 is all tested
            }
        }
    }
}

This is integration of rigidbody:

void Rigidbody::integrate(float dt){
    if (_inverseMass == 0.0f) return;
    auto trans = thisEntity()->transform();

    //sleep
    if (glm::length(_velocity) < 3.5f)  ++_sleepStage;
    else                                _sleepStage = 0;

    if (_sleepStage > 180) { _awake = false; return; }

    std::cout << "# " << glm::length(_velocity) << " " << _sleepStage << std::endl;

    //euler integration of position
    _velocity.y -= _gravity * dt;           //rhs = ubrzanje * dt, lhs = brzina
    trans->addPosition(_velocity * dt);     //rhs = brzina * dt, lhs = pozicija
}

This function is called to resolve collision:

inline void Physics::collisionResolution(Collider* c1, Collider* c2, glm::vec3 mtvAxis, float mtvDist, bool flip){
    auto A = c1->getRigidbody();
    auto B = c2->getRigidbody();

    //COLLISION RESOLUTION
    if (flip) mtvAxis *= -1.f;
    mtvDist /= 2.f;

    if(A->_inverseMass > 0.0f && A->isAwake())
        c1->getEntity()->transform()->addPosition(mtvAxis * mtvDist);
    if(B->_inverseMass > 0.0f && B->isAwake())
        c2->getEntity()->transform()->addPosition(-mtvAxis * mtvDist);

    //LINEAR IMPULSE
    glm::vec3 rv = A->_velocity - B->_velocity;
    float velAlongNormal = glm::dot(rv, mtvAxis);
    if (velAlongNormal > 0.0f) return;

    float e = 0.2f;
    float j = -(1.0f + e) * velAlongNormal;
    j /= A->_inverseMass + B->_inverseMass;

    glm::vec3 impulse = mtvAxis * j;

    glm::vec3 impulseA = impulse * A->_inverseMass;
    glm::vec3 impulseB = impulse * B->_inverseMass;

    A->setAwake();  //sets _awake to true
    A->_velocity += impulseA;

    B->setAwake();
    B->_velocity -= impulseB;

    //FRICTION
    glm::vec3 t = rv - (mtvAxis * velAlongNormal);

    if (glm::length(t) == 0.0f) return;
    t = glm::normalize(t);

    float jt = -glm::dot(rv, t);
    jt /= A->_inverseMass + B->_inverseMass;
    if (jt == 0.0f) return;

    float friction = glm::sqrt(0.03f);
    if (jt > j * friction)
        jt = j * friction;
    else if (jt < -j * friction)
        jt = -j * friction;

    glm::vec3 tangentImpuse = t * jt;
    A->_velocity += tangentImpuse * A->_inverseMass;
    B->_velocity -= tangentImpuse * B->_inverseMass;

    //ANGULAR IMPULSE
    //THE DARKEST BLACK MAGIC
}
\$\endgroup\$
0
\$\begingroup\$

What if you detect "stillness" not via velocity, but via forces instead?

When laying on top of the stack, the block's aggregate force is zero, as the gravity force and the supporting force from block below will cancel each other out perfectly.

So yeah, you don't want collision tests when sleeping, indeed.

Maybe the system can keep track of which rigid bodies it was touching at the moment it went to sleep?

If any of those start to move, that body would need waking up again.

It would get a bit messy to express in code though, as it is a variable length list.

\$\endgroup\$
  • 1
    \$\begingroup\$ The trick is this reduces the savings you get from "sleeping" objects since you still need to check for stack collisions and accumulate forces every frame. Ideally, we'd like to be able to put these objects aside entirely until something changes in their neighborhood, doing no movement calculations on sleeping objects at all, and checking for no collisions between sleeping/static objects. \$\endgroup\$ – DMGregory Dec 18 '18 at 19:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.