Войти
IrrlichtСтатьи

Пермещение по сферической поверхности (2 стр)

Автор:

Текст программы

Теперь, когда все пояснения сделаны, пора перейти к тексту самой программы. Предполагается, что все настройки среды программирования уже сделаны, указаны все пути.

#include <irrlicht.h>
#include <deque>
using namespace std;
using namespace irr;

#pragma comment(lib, "Irrlicht.lib")
typedef core::vector3df vec; // Переопределяем тип, чтобы попроще было писать

#define SPHERE_RADIUS  10 // Радиус планеты
#define OBJECT    10 // Количество объектов

scene::IAnimatedMeshSceneNode* sphere = 0; // Меш планеты
scene::IAnimatedMeshSceneNode* sphere2 = 0; // Меш спутника
IrrlichtDevice* device = 0;
video::IVideoDriver* driver;
scene::ISceneManager* smgr;
irr::scene::IBillboardSceneNode *sun; // Билбоард солнца

#define DEG    (3.14/180) // константа для перевода из градусов в радианы и наоборот

Тут вопросов не должно возникнуть, просто добавляем библиотеки, создаем переменные. Далее создадим класс нашего объектика

class Object
{
public:
  scene::ISceneNode* node; // Меш самого объекта
  scene::ISceneNode* cSys; // Корневая нода, которая определяет положение объекта
  Object()
  {
    cSys = smgr->addEmptySceneNode();
    node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("obj1.3ds"));
    node->setMaterialTexture(0, driver->getTexture("tex1.jpg"));
    node->setMaterialFlag(video::EMF_LIGHTING, false);
    node->setPosition(vec(0,SPHERE_RADIUS,0));
    cSys->addChild(node);
    cSys->setRotation(vec(rand()%360,rand()%360,rand()%360));
  }
};

Об этом уже было подробно расписано выше. Создаем пустую ноду, создаем ноду с мешем объекта, к пустой ноде приделываем ноду с объектом. Ноде с мешем задаем координаты. А пустой ноде задаем случайную ориентацию.
Далее создаем класс камеры, в которой опишем все создаваемые ноды и обработку перемещений камеры.

class Camera
{
public:
  float x, y, xt, yt, z, zt;
  scene::ICameraSceneNode *node;
  scene::ISceneNode *cSys, *target;
  core::matrix4 mat;
  vec pos;
  Camera()
  {
    x = 0;
    y = 0;
    z = 0;
    xt = 0;
    yt = 0;
    zt = 0;
    node = smgr->addCameraSceneNode(NULL, vec(0,15,-2), vec(0,0,0));
    cSys = smgr->addEmptySceneNode();
    target = smgr->addEmptySceneNode();
    target->setPosition(vec(0, 10, 3));
    cSys->addChild(node);
    cSys->addChild(target);

    mat.makeIdentity();
  }
  void move()
  {
    vec v;
    core::matrix4 m;

    x+=xt;
    y+=yt;
    z+=zt;
    x*=0.99f;
    y*=0.99f;
    z*=0.99f;
    m.setRotationDegrees(vec(x,0,0));
    mat *= m;
    m.setRotationDegrees(vec(0,y,0));
    mat *= m;
    m.setRotationDegrees(vec(0,0,z));
    mat *= m;

    v = mat.getRotationDegrees();
    cSys->setRotation(v);

    pos = target->getAbsolutePosition();
    node->setUpVector(pos);
    node->setTarget(pos);
  
  }
};

Переменная mat – это наша самая главная матрица, которая хранит положение нашей камеры, а точнее ориентацию корневой ноды.
Переменные xt, yt, zt  - это что-то вроде ускорения в определенном направлении. Они равны чему-то если соответствующая клавиша нажата и равны 0 если отпущена. Переменные x, y, z - это текущая скорость. Конструкция типа
x*=0.99f;
введена для того, чтобы создать эффект инерции. Далее следует
m.setRotationDegrees(vec(x,0,0));
mat *= m;
Здесь мы создаем матрицу поворота вокруг определенной оси и умножаем нашу самую главную матрицу на только что созданную.
  v = mat.getRotationDegrees();
  cSys->setRotation(v);
В этом фрагменте мы преобразуем нашу супер-матрицу в углы Эйлера и задаем ориентацию корневой ноды с помощью этих углов. Все это делается для потому, что в IrrLicht нельзя задать ориентацию объекта непосредственно из матрицы (во всяком случае я не нашел такой функции). Вот и приходится делать одно лишнее преобразование.
  pos = target->getAbsolutePosition();
  node->setUpVector(pos);
  node->setTarget(pos);
Об этом уже было сказано выше. Мы определяем глобальные координаты целевой ноды, и задаем этот вектор в качестве цели для камеры. Затем задаем вертикальный вектор для камеры, для чего идеально подходит тот же самый вектор pos.
Что происходит в конструкторе поясню вкратце. Создаем ноду камеры, корневую пустую ноду, и пустую ноду цели. Ноду камеры и ноду цели прикрепляем к корневой пустой ноде. Задаем им координаты.

class MyEventReceiver : public IEventReceiver
{
public:
  Camera *camera;
  MyEventReceiver()
  {
    camera = NULL;
  }
  void init(Camera *c)
  {
    camera = c;  
  }
  virtual bool OnEvent(SEvent event)
  {
    if(event.EventType == EET_KEY_INPUT_EVENT)
      if(event.KeyInput.PressedDown)
        switch(event.KeyInput.Key)
        {
        case KEY_KEY_W:
          camera->xt=.0009f;
          break;
        case KEY_KEY_S:
          camera->xt=-.0006f;
          break;
        case KEY_KEY_Q:
          camera->zt=.0009f;
          break;
        case KEY_KEY_E:
          camera->zt=-.0006f;
          break;
        case KEY_KEY_A:
          camera->yt=-.0016f;
          break;
        case KEY_KEY_D:
          camera->yt=.0016f;
          break;
        }
      else
        switch(event.KeyInput.Key)
        {
        case KEY_KEY_Q:
        case KEY_KEY_E:
          camera->zt = 0;
          break;
        case KEY_KEY_A:
        case KEY_KEY_D:
          camera->yt = 0;
          break;
        case KEY_KEY_W:
        case KEY_KEY_S:
          camera->xt = 0;
          break;
        }
    return false;
  }
};

Здесь мы создаем класс, который будет обрабатывать все наши нажатия клавишь. Значения скоростей он будет записывать непосредственно в объект класса Camera. Тут все просто, нажали клавшу - придали ускорение. Отпустили – сбросили ускорение.

И последний фрагмент на сегодня. Он побольше, но не на много сложнее. Читайте комментарии.

int main()
{
  MyEventReceiver receiver;

  device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480),
    16, false, false, false, &receiver);

  driver = device->getVideoDriver();
  smgr = device->getSceneManager();

// инициализируем переменные
  Camera camera;
  Object obj[OBJECT];
  receiver.init(&camera);
  driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

  // создаем звездное небо
  smgr->addSkyBoxSceneNode(
    driver->getTexture("tex2.jpg"),
    driver->getTexture("tex2.jpg"),
    driver->getTexture("tex2.jpg"),
    driver->getTexture("tex2.jpg"),
    driver->getTexture("tex2.jpg"),
    driver->getTexture("tex2.jpg"));

// создаем билбоард с солнышком
  sun = smgr->addBillboardSceneNode (NULL, core::dimension2d< f32 >(500.0f, 500.0f), vec(300,300,300));
  sun->setMaterialTexture(0, driver->getTexture("tex6.jpg"));
  sun->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); 
  sun->setMaterialFlag(video::EMF_LIGHTING, false);
  
  // создаем меш планеты
  sphere = smgr->addAnimatedMeshSceneNode(smgr->getMesh("sphere.3ds"));
  sphere->setScale(vec(SPHERE_RADIUS,SPHERE_RADIUS,SPHERE_RADIUS));
  sphere->setMaterialTexture(0, driver->getTexture("tex5.jpg"));
  sphere->setMaterialFlag(video::EMF_LIGHTING, false);

  // создаем меш спутника и аниматоры к нему
  scene::ISceneNode *n = smgr->addEmptySceneNode();
  sphere2 = smgr->addAnimatedMeshSceneNode(smgr->getMesh("sphere.3ds"));
  sphere2->setScale(vec(SPHERE_RADIUS,SPHERE_RADIUS,SPHERE_RADIUS));
  sphere2->setMaterialTexture(0, driver->getTexture("tex3.jpg"));
  sphere2->setMaterialFlag(video::EMF_LIGHTING, false);
  sphere2->setPosition(vec(70,0,0));
  n->addChild(sphere2);
  scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(vec(0.03f, 0, 0.03f));
  n->addAnimator(anim);
  anim->drop();
  anim = smgr->createRotationAnimator(vec(0.3f, 0.0f, 0.0f));
  sphere2->addAnimator(anim);
  anim->drop();

  int lastFPS = -1;
  while(device->run())
  {
    driver->beginScene(true, true, video::SColor(255,113,113,133));

    smgr->drawAll();
    driver->endScene();

    camera.move(); // обрабатываем движение камеры

    // стандартный фрагмент с выводом fps в заголовок окна
    int fps = driver->getFPS();
    if (lastFPS != fps)
    {
      wchar_t tmp[1024];
      swprintf(tmp, 1024, L"Sphere - Irrlicht Engine [%ls] fps: %d", 
        driver->getName(), fps);
      device->setWindowCaption(tmp);
      lastFPS = fps;
    }
  }
  device->drop();
  return 0;
}

Исходный код программы можно скачать по этой ссылке. Для компиляции проекта вам понадобятся исходники IrrLicht.
По этой ссылке находится более продвинутый вариант программы.

Страницы: 1 2

11 ноября 2008

Комментарии [2]