Haaf's Game EngineСтатьи

Отрисовка углового сектора текстуры (3 стр)

Автор:

Ну и, в завершение, функция отрисовки углового сектора:

void RenderAngularSector(HGE* hge
, hgeSprite* sprite // можно спрайт передать
, hgeAnimation* animation // а можно анимацию
, int screen_x // точка, в которой рисовать на экране
, int screen_y
, int x_start // точка внутри прямоугольника (относительные координаты)
, int y_start
, double origAngleA // начальное значение угла (радиан)
, double origAngleB // конечное значение угла (радиан)
)
{
// Let's rock!
  HTEXTURE tex = 0;
  int blend = BLEND_DEFAULT;

  float x,y,w,h; // получаем актуальные координаты по точкам на текстуре
  int tex_width = 0;
  int tex_height = 0;

  if(sprite)
  {
    // у меня в закомментированных переменных хранится начальное смещение
    // текстурных координат, передаваемое в конструктор hgeSprite
    x = 0; //this->m_iTextureX;
    y = 0; //this->m_iTextureY;
  
    tex = sprite->GetTexture();
    blend = sprite->GetBlendMode();

    // Это кака и нагружает! На самом деле я все это храню в классе.
    w = sprite->GetWidth();
    h = sprite->GetHeight();
    tex_width = hge->Texture_GetWidth(tex,true);
    tex_height = hge->Texture_GetHeight(tex,true);

  }
  else if(animation)
  {
    // у меня в закомментированных переменных хранится начальное смещение
    // текстурных координат, передаваемое в конструктор hgeAnimation
    x = 0; //this->m_iTextureX;
    y = 0; //this->m_iTextureY;

    tex = animation->GetTexture();
    blend = animation->GetBlendMode();
    
    // Это кака и нагружает! На самом деле я все это храню в классе.
    w = animation->GetWidth();
    h = animation->GetHeight();
    tex_width = hge->Texture_GetWidth(tex,true);
    tex_height = hge->Texture_GetHeight(tex,true);

    // получили номер текущего кадра
    int cur_frame = animation->GetFrame();

    // высчитываем координаты сдвига относительно переданной точки
    int ncols = tex_width / w;
    int tx1 = x + cur_frame*w;
    int ty1 = y;

    if(tx1 > tex_width-w)
    {
      cur_frame -= int(tex_width - x) / int(w);
      tx1 = w * (cur_frame%ncols);
      ty1 += h * (1 + cur_frame/ncols);
    } // if

    // тут получили координаты текущего фрейма анимации в текстуре
    x = tx1;
    y = ty1;

  } // if(animation)
  
  // транслируем относительные координаты точки внутри прямоугольника в абсолютные
  x_start += x;
  y_start += y;

  const double MAGIC = M_PI*2; // для трансляции направления вектора в правильную сторону, 
  // т.к. у нас координаты 0*PI в радианах справа

  // теперь транслируем углы в нормальные координаты, чтобы правильно 
  // работал тест пересечений со сторонами
  double angleA = MAGIC - origAngleA;
  double angleB = MAGIC - origAngleB;
  
  // идем по часовой стрелке от первого угла ко второму
  int vec_len = max(w,h)*2; // получаем длину вектора, чтобы он гарантированно пересекся со сторонами

  // вычисляем конечную точку первого вектора
  double x_end = x_start + cosf(angleA)*vec_len;
  double y_end = y_start + sinf(angleA)*vec_len;

  hgeVector startPoint(x_start,y_start); // начальная точка векторов
  hgeVector endPoint1(x_end,y_end); // конечная точка первого вектора

  // вычисляем конечную точку второго вектора
  x_end = x_start + cosf(angleB)*vec_len;
  y_end = y_start + sinf(angleB)*vec_len;

  hgeVector endPoint2(x_end,y_end); // конечная точка второго вектора

  // создаем вектора вершин прямоугольника
  hgeVector topLeftCorner(x,y); // левый/верхний угол
  hgeVector topRightCorner(x+w,y); // правый/верхний угол
  hgeVector rightBottomCorner(x+w,y+h); // правый/нижний угол
  hgeVector leftBottomCorner(x,y+h); // левый/нижний угол

  // точки вершин прямоугольника, пригодятся для обхода вершин
  hgeVector rectCoords[4] = {topLeftCorner,topRightCorner,rightBottomCorner,leftBottomCorner};
  std::vector<side_point> points; // вычисленные точки

  // проверяем первый вектор (для угла a)
  bool cross_found = false;

  // пересекаемся справа?
  CROSS_RESULT cr = Crossing(startPoint,endPoint1,topRightCorner,rightBottomCorner);
  if(cr.type == ctOnBounds || cr.type == ctInBounds)
  {
    // пересекаемся справа!
    side_point s; s.side = wsRight; s.pt = cr.pt; points.push_back(s); cross_found = true;
  }
  
  if(!cross_found)
  {
    // пересекаемся снизу?
    cr = Crossing(startPoint,endPoint1,leftBottomCorner,rightBottomCorner);
    if(cr.type == ctOnBounds || cr.type == ctInBounds)
    {
      // пересекаемся снизу!
      side_point s; s.side = wsBottom; s.pt = cr.pt; points.push_back(s); cross_found = true;
    }
  }

  if(!cross_found)
  {
    // пересекаемся слева?
    cr = Crossing(startPoint,endPoint1,topLeftCorner,leftBottomCorner);
    if(cr.type == ctOnBounds || cr.type == ctInBounds)
    {
      // пересекаемся слева!
      side_point s; s.side = wsLeft; s.pt = cr.pt; points.push_back(s); cross_found = true;
    }
  }

  if(!cross_found)
  {
    // пересекаемся сверху?
    cr = Crossing(startPoint,endPoint1,topLeftCorner,topRightCorner);
    if(cr.type == ctOnBounds || cr.type == ctInBounds)
    {
      // пересекаемся сверху!
      side_point s; s.side = wsTop; s.pt = cr.pt;  points.push_back(s); cross_found = true;
    }
  }
  
  // все, теперь мы имеем в векторе начальную точку на одной из сторон.
  // теперь надо получить сторону, на которой точка второго вектора.

  side_point secondVecCross;
  cross_found = false;

  // пересекаемся справа?
  cr = Crossing(startPoint,endPoint2,topRightCorner,rightBottomCorner);
  if(cr.type == ctOnBounds || cr.type == ctInBounds)
  {
    // пересекаемся справа!
    secondVecCross.side = wsRight; secondVecCross.pt = cr.pt; cross_found = true;
  }

  if(!cross_found)
  {
    // пересекаемся снизу?
    cr = Crossing(startPoint,endPoint2,leftBottomCorner,rightBottomCorner);
    if(cr.type == ctOnBounds || cr.type == ctInBounds)
    {
      // пересекаемся снизу!
      secondVecCross.side = wsBottom; secondVecCross.pt = cr.pt; cross_found = true;
    }
  }

  if(!cross_found)
  {
    // пересекаемся слева?
    cr = Crossing(startPoint,endPoint2,topLeftCorner,leftBottomCorner);
    if(cr.type == ctOnBounds || cr.type == ctInBounds)
    {
      // пересекаемся слева!
      secondVecCross.side = wsLeft; secondVecCross.pt = cr.pt; cross_found = true;
    }
  }

  if(!cross_found)
  {
    // пересекаемся сверху?
    cr = Crossing(startPoint,endPoint2,topLeftCorner,topRightCorner);
    if(cr.type == ctOnBounds || cr.type == ctInBounds)
    {
      // пересекаемся сверху!
      secondVecCross.side = wsTop; secondVecCross.pt = cr.pt; cross_found = true;
    }
  }

  // тут мы имеем две точки на сторонах прямоугольника. По часовой стрелке
  // проходим по всем вершинам между первой и второй точками и сохраняем их
  // в массив для будущего использования. 

  // ВСЕ ТОЧКИ УЖЕ В АВСОЛЮТНЫХ ТЕКСТУРНЫХ КООРДИНАТАХ!

  if(points.size() > 0) // есть первая точка
  {
    side_point firstVecCross = points[0]; // точка пересечения первого вектора
    int which_side = firstVecCross.side; // счетчик вершин
    // в which_side индекс, на какой стороне лежит точка.
    // если она лежит на левой стороне - следующая точка - левый/верхний угол и т.д.

    // если не было заворота за хотя бы одну вершину - угол слишком мал,
    // и не надо делать do, надо делать while !!!

    bool has_wrap = false; // флаг заворота за вершину

    if(firstVecCross.side == secondVecCross.side) // на одной стороне прямоугольника очутились
    {

      if(firstVecCross.side == wsRight) // на правой стороне столкнулись
      {
        // если вторая точка лежит выше - быть обходу вершин
        if(secondVecCross.pt.y < firstVecCross.pt.y)
          has_wrap = true;
      }
      else if(firstVecCross.side == wsTop) // на верхней стороне столкнулись
      {
        // если вторая точка лежит левее - быть обходу вершин
        if(secondVecCross.pt.x < firstVecCross.pt.x)
          has_wrap = true;
      }
      else if(firstVecCross.side == wsLeft) // на левой стороне столкнулись
      {
        // если вторая точка лежит ниже - быть обходу вершин
        if(secondVecCross.pt.y > firstVecCross.pt.y)
          has_wrap = true;
      }
      else if(firstVecCross.side == wsBottom) // на нижней стороне столкнулись
      {
        // если вторая точка лежит правее - быть обходу вершин
        if(secondVecCross.pt.x > firstVecCross.pt.x)
          has_wrap = true;
      }
    } // if

    // проходим по всем точкам, пока не достигнем стороны, на которую опирается второй вектор.
    if(!has_wrap) // не было заворота второго вектора за одну из вершин
    {
      while(which_side != secondVecCross.side) 
      {

        // теперь в which_side у нас лежит индекс вершины, который надо добавить к точкам
        side_point new_pt;
        new_pt.pt = rectCoords[which_side];

        points.push_back(new_pt); // добавляем индекс вершины

        // прибавляем индекс и смотрим за переполнением.
        which_side = which_side + 1;
        if(which_side > wsBottom) // переполнились, начинаем по кругу
          which_side = wsLeft;

      } // while

    }
    else // был заворот второго угла хотя бы за одну вершину
    {
      do
      {

        // теперь в which_side у нас лежит индекс вершины, который надо добавить к точкам
        side_point new_pt;
        new_pt.pt = rectCoords[which_side];

        points.push_back(new_pt); // добавляем индекс вершины

        // прибавляем индекс и смотрим за переполнением.
        which_side = which_side + 1;
        if(which_side > wsBottom) // переполнились, начинаем по кругу
          which_side = wsLeft;

      } while(which_side != secondVecCross.side);
    }

    // добавляем конечную точку
    points.push_back(secondVecCross);

    // теперь у нас есть все точки пересечения векторов, включая точки вершин,
    // следовательно мы можем строить треугольники.

    // проходим по всем точкам слева-направо, берем соседние вершины и
    // используем их для треугольника.

    for(size_t i =1;i< points.size();i++)
    {
      side_point point1 = points[i-1];
      side_point point2 = points[i];

      // начинаем со startPoint, через point1 к point2 - это и есть очередной треугольник.
      // при отрисовке учитываем сдвиг экранных координат и то, что все координаты - 
      // абсолютные текстурные, и при выводе на экран сдвигом x,y надо пренебречь.

      hgeTriple triple;
      triple.tex = tex;
      triple.blend = blend;

      triple.v[0].col = 0xFFFFFFFF;
      triple.v[1].col = 0xFFFFFFFF;
      triple.v[2].col = 0xFFFFFFFF;
      triple.v[0].z = 1.0;
      triple.v[1].z = 1.0;
      triple.v[2].z = 1.0;

      triple.v[0].x = startPoint.x - x + screen_x; 
      triple.v[0].y = startPoint.y - y + screen_y;
      triple.v[0].tx = startPoint.x/tex_width; 
      triple.v[0].ty = startPoint.y/tex_height;

      triple.v[1].x = point1.pt.x - x + screen_x; 
      triple.v[1].y = point1.pt.y - y + screen_y;
      triple.v[1].tx = point1.pt.x/tex_width; 
      triple.v[1].ty = point1.pt.y/tex_height;

      triple.v[2].x = point2.pt.x - x + screen_x; 
      triple.v[2].y = point2.pt.y - y + screen_y;
      triple.v[2].tx = point2.pt.x/tex_width; 
      triple.v[2].ty = point2.pt.y/tex_height;
      

      // рисуем треугольник
      hge->Gfx_RenderTriple(&triple);

    } // for
    
  } // if(points.size() > 0)

}

Ну и, как результат всей этой халабуды, вызов

RenderAngularSector(hge, sprite, NULL, 100, 100, 128, 128, M_PI/2 , M_PI);

для спрайта размерами 256x256 должен вывести текстуру с выпиленной верхней/левой четвертью. В следующий части  будет пример с исходником.

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

Страницы: 1 2 3 4 Следующая »

2 января 2011

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