unit MX2;
/////////////////////////////////////////
// Object Transform Model Animetion unit
// by XProger
/////////////////////////////////////////
interface

uses
  Windows, OpenGL, eXgine, l_math;

type
// кадр анимации
  TFrame = record
    Pos : TVector;  // позиция
    Rot : TQuat;    // поворот
  end;

// анимируемый объект
  TMesh = object
    V_Count  : Integer;            // кол-во вершин
    F_Count  : Integer;            // кол-во граней
    T_Count  : Integer;            // кол-во текстурных координат
    Vertex   : array of TVector;   // вершины
    Face     : array of TFace;     // грани
    TexCoord : array of TVector2D; // текстурные координаты
    TexFace  : array of TFace;     // текстурные грани
    Frame    : array of TFrame;    // кадры анимации
  end;

// модель
  TModel = class
    constructor Create(const Name: string);
   private
    Texture : TTexture;       // идентификатор текстуры
    FPS     : Integer;        // кол-во кадров анимации в секунду
    K_Count : Integer;        // кол-во кадров анимации (Mesh.Frame)
    M_Count : Integer;        // кол-во анимируемых объектов
    Mesh    : array of TMesh; // анимируемые объекты
   public
    procedure Render;
  end;

implementation

constructor TModel.Create(const Name: string);
var
  F : File;
  i : Integer;
begin
  AssignFile(F, Name + '.mx2');
  Reset(F, 1);
// читаем кол-во объектов
  BlockRead(F, M_Count, SizeOf(M_Count));
  SetLength(Mesh, M_Count);
// параметры анимации
  BlockRead(F, K_Count, SizeOf(K_Count));
  BlockRead(F, FPS, SizeOf(FPS));
// циклом считываем все объекты
  for i := 0 to M_Count - 1 do
    with Mesh[i] do
    begin
    // Чтение заголовка
      BlockRead(F, V_Count, SizeOf(V_Count));
      BlockRead(F, F_Count, SizeOf(F_Count));
      BlockRead(F, T_Count, SizeOf(T_Count));
    // Выделение памяти под данные объекта
      SetLength(Vertex, V_Count);
      SetLength(Face, F_Count);
      SetLength(TexCoord, T_Count);
      SetLength(TexFace, F_Count);
      SetLength(Frame, K_Count);
    // Грани и текстурные координаты
      BlockRead(F, Vertex[0], V_Count * SizeOf(TVector));
      BlockRead(F, Face[0], F_Count * SizeOf(TFace));
      BlockRead(F, TexCoord[0], T_Count * SizeOf(TVector2D));
      BlockRead(F, TexFace[0], F_Count * SizeOf(TFace));
      BlockRead(F, Frame[0], K_Count * SizeOf(TFrame));
    end;
  CloseFile(F);
// Загрузка текстуры (eXgine функция)
  Texture := tex.Load(PChar(Name + '.jpg'));
end;

procedure TModel.Render;
var
  i, j   : Integer;
  lf, nf : Integer;
  dt, t  : Single;
  Matrix : TMatrix;
  p      : TVector;
  r      : TQuat;
begin
  dt := 1000 div FPS;                       // длительность одного кадра
  lf := trunc(GetTickCount/dt) mod K_Count; // вычисляем предыдущий кадр
  nf := (lf + 1) mod K_Count;               // и следующий
  t  := frac(GetTickCount/dt);              // временной коэффициент

// отрисовка
  tex.Enable(Texture); // Включение текстуры (eXgine функция)
  for j := 0 to M_Count - 1 do // цикл по всем объектам
    with Mesh[j] do
    begin
    // линейная интерполяция кватерниона и позиции
      r := Q_Lerp(Frame[lf].Rot, Frame[nf].Rot, t);
      p := V_Lerp(Frame[lf].Pos, Frame[nf].Pos, t);      
    // расчёт матрицы трансформации для объекта
      Q_Matrix(r, Matrix);    // перевод кватерниона в матрицу
      Matrix[3][0] := p.X;    // дописываем позицию в последнюю строку
      Matrix[3][1] := p.Y;
      Matrix[3][2] := p.Z;
    // отрисовка
      glPushMatrix;
      glMultMatrixf(@Matrix); // применяем матрицу
      glBegin(GL_TRIANGLES);
      for i := 0 to F_Count - 1 do
      begin
        glTexCoord2fv(@TexCoord[TexFace[i][0]]); glVertex3fv(@Vertex[Face[i][0]]);
        glTexCoord2fv(@TexCoord[TexFace[i][1]]); glVertex3fv(@Vertex[Face[i][1]]);
        glTexCoord2fv(@TexCoord[TexFace[i][2]]); glVertex3fv(@Vertex[Face[i][2]]);
      end;
      glEnd;
      glPopMatrix;
    end;
end;


end.
