Ogre + Bullet: проверка столкновения камеры с землей

Хочу предложить более совершенный алгоритм проверки коллизии камеры с землей, чем опубликованный ранее (ссылка). Теперь все как «у взрослых» (так сделано, например, в Perfect World). Задача: камера не должна проходить сквозь стены.

Оговорим обозначения:

  • cameraPos — позиция камеры
  • targetPos — позиция цели (за которой следит камера)
  • leftTop, rightTop, leftBottom, rightBottom — позиции крайних точек экрана в мировых координатах
  • hitPoint — точка коллизии луча с землей

В общих чертах, алгоритм такой:

  • вычисляем координаты leftTop, rightTop, rightBottom, leftBottom
  • для каждой крайней точки p создаем луч, начало которого в targetPos, а окончание в p
  • для каждого луча проверяем столкновение с землей, назовем точку столкновения hitPoint
  • вычисляем минимальную длину проекции вектора (hitPointtargetPos) на вектор (cameraPostargetPos)
  • новая позиция камеры — это targetPos смещенная в направлении cameraPos на минимальную длину проекции

А теперь выразим это в коде (код писался прямо в блокноте, поэтому не обессудьте)

bool checkCameraCollides( const Ogre::Vector3 & cameraPos, 
    const Ogre::Vector3 & targetPos, Ogre::Vector3 & newCameraPos )
{
    // Сохраняем позицию и направление камеры
    const Ogre::Vector3 lastCameraPos = camera->getPosition();
    const Ogre::Vector3 lastCameraDir = camera->getDirection();
 
    // Установим новую позицию и направление камеры для корректной 
    // работы метода Ogre::Camera::getCameraToViewportRay()
    camera->setPosition(cameraPos);
    camera->setDirection(targetPos - cameraPos);
 
    // Вычисляем крайние точки
    std::vector<Ogre::Vector3> points;
    {
        Ogre::Ray ray;
        // Верхний левый угол
        camera->getCameraToViewportRay(0, 0, &ray);
        points.push_back(ray.getOrigin());
        // Нижний левый угол
        camera->getCameraToViewportRay(0, 1, &ray);
        points.push_back(ray.getOrigin());
        // Верхний правый угол
        camera->getCameraToViewportRay(1, 0, &ray);
        points.push_back(ray.getOrigin());
        // Нижний правый угол
        camera->getCameraToViewportRay(1, 1, &ray);
        points.push_back(ray.getOrigin());
    }
 
    // Вернем камере исходные позицию и направление
    camera->setPosition(lastCameraPos);
    camera->setDirection(lastCameraDir);
 
    // Минимальная длина проекции
    float minProj = 0.0f;
    // Есть столкновение
    bool collideDetected = false;
    // Луч от цели до камеры
    const Ogre::Vector3 targetToCameraDir = 
        (cameraPos - targetPos).normalisedCopy();
 
    for (size_t = 0; i < points.size(); ++i)
    {
        // Начало луча в позиции цели
        const btVector3 from = OgreBtConverter::to(targetPos);
        // Окончание луча крайней точке
        const btVector3 to = OgreBtConverter::to(points[i]);
 
        // Настроим стуктуру для обнаружение столкновения
        btDynamicsWorld::ClosestRayResultCallback rayCallback(from, to);
        // Сталкиваться только с землей
        rayCallback.m_collisionFilterGroup = CG_GROUND;
        rayCallback.m_collisionFilterMask = CM_GROUND;
        // Пускаем луч
        dynamicsWorld->rayTest(from, to, rayCallback);
 
        // Есть контакт
        if (rayCallback.hasHit())
        {
            // Точка столкновения
            const Ogre::Vector3 hitPoint = 
                BtOgreConverter::to(rayCallback.m_hitPointWorld);
 
            // Длина проекции на луч от targetPos до cameraPos
            float proj = (hitPoint - targetPos).dotProduct(targetToCameraDir);
 
            // Сохраним минимальную длину проекции
            if (!collideDetected || minProj > proj)
            {
                minProj = proj;
                collideDetected = true;
            }
        }
    }
 
    // Если столкновение произошло
    if (collideDetected)
    {
        // Новая позиция камеры - это targetPos смещенная в сторону cameraPos на minProj
        newCameraPos = targetPos + targetToCameraDir * minProj;
        return true;
    }
 
    return false;
}

И для «самых маленьких» пример использования:

void updateCameraPosition()
{
    Ogre::Vector3 cameraPos, targetPos, newCameraPos;
    // Вычисляем позицию камеры и цели (решение о том, как это делается, 
    // остается за вами, у меня позиция и цель камеры зависит от режима камеры)
    calculateCameraPosition(cameraPos, targetPos);
    // Проверим столкновение
    if (checkCameraCollides(cameraPos, targetPos, newCameraPos))
    {
        camera->setPosition(newCameraPos);
    }
    else
    {
        camera->setPosition(cameraPos);
    }
    camera->setDirection(targetPos - camera->getPosition());
}
Как просверлить квадратное отверстие? Теория и практика

Оказывается, есть способ сверления квадратных отверстий. Ниже подробное описание теории в картинках и видео реальной работы.

Теория

Практика

Источник видео с теорией: «Математические этюды».

Как научиться шевелить ушами. Теория и практика

Иногда меня просят показать шевеление ушами. Но никто не спрашивает как я это делаю. Расскажу вкратце как это делается. Вы смотрели «Полицейскую академию»? Нет? Ну и дурак зря.

Джордж Гейнс Прошу любить и жаловать, комендант Эрик Лассард (в быту Джордж Гейнс). Именно этот человек в каком-то 199x году заставил меня задуматься, как двигать ушами самому.

В те далекие времена у меня было очень много свободного времени, как у того медвежонка из анекдота. Домашнее задание я практически не делал, потому что прогуливал школу периодически. Ремонт в квартире происходил без моего участия, да и запчасти на машину не надо было искать по всему городу, потому что велосипед. А велосипед «Кама», как известно, хрен сломаешь.

Оказалось, что двигать ушами довольно просто. Многие делают это неосознанно. Видели как у военного или стража порядка шевелится фуражка, когда он удивлен? Ты ему: «Да не было тут никогда одностороннего движения», а он тебе так р-р-аз фуражкой недовольно пошевелит: «Пройдемте, мол, уважаемый, в машину, изымем права». А животное (млекопитающее) шевелит ушами ежедневно помногу раз, особенно если эта скотина сожрала все вкусные конфетки со стола, раскидала мусорное ведро по всей кухне и растрепала твой любимый пуфик.

Чтоб подвигать ушами, надо сильно удивиться, не выражая эмоций лицом. На первых порах лучше использовать фуражку или шапку. Постепенно через многие годы изматывающих тренировок перед зеркалом можно научиться шевелить одно ухо, потом другое затем третье.

Вам кажется это глупостью, но у каждого есть группы мыщц, о которых мы ни сном, ни духом. Это можно ощутить после тренировок в спортзале с опытным тренером. Мыщцы обнаруживаются в самых неожиданных местах, например под ребрами, хотя зачем они там нужны.. Наверно девок на пляже кадрить

А еще можно двигать носом влево вправо и вниз, но об этом в другой раз.

«Толерантность» как много в этом звуке для сердца русского слилось

Челнок пришвартовался к звездолету, и пассажиры расселись по местам, ожидая высадки на планету. Джаммар, стараясь сохранять, как и положено мужчине, равнодушное и непроницаемое выражение лица, прошел по рядам и наконец нашел свое кресло. Его соседкой справа оказалась пожилая женщина.
— В первый раз высаживаетесь на новую планету, молодой человек? — спросила она его, улыбаясь.
Джаммар, раздосадованный тем, что его неопытность так легко заметить, молча кивнул и отвернулся, показывая, что не желает продолжать разговор. «Как не стыдно! — подумал он. — Пожилая женщина, а пользуется косметикой и даже не покрыла голову платком! Как какая-нибудь шлюха!»

Маленький рассказ про правильное применение толерантности и политкорректности: «Уважение культурных традиций» автора Шапиро Максима Анатольевича (а вот его ЖЖ).

Bullet Game Physics изменение позиции и ориентации физического тела

Не буду долго размусоливать. Приведу сразу код:

void setTransform( btRigidBody * body, const btVector3 & position, const btQuaternion & rotation )
{
    // Матрица транформации
    btTransform transform;
    transform.setIdentity();
 
    // Заполним матрицу
    transform.setOrigin(position);
    transform.setRotation(rotation);
 
    // Применим матрицу к телу
    body->setWorldTransform(transform);
 
    // Важный этап: обнулим линейную и угловую скорости
    body->setLinearVelocity(btVector3(0, 0, 0));
    body->setAngularVelocity(btVector3(0, 0, 0));
}

Для чего требуется обнуление скоростей? Догадайтесь сами.

Блог Евгения Жирнова