unit uPOA;

interface

uses
   uGlobal, VectorTypes;

type
  TGLObject = packed record
    ID: integer;
    ObjType: TGLObjectType;
    Position, Rotation, Scale: TVector4f;
    Params: packed array of variant;
  end;
  TGLTransform = record
    ObjID: integer;
    Position, Rotation, Scale: TVector4f;
  end;
  TPOAObject = class
  var
    FVisible: boolean;

    procedure SetVisible(Visible: boolean);
  public
    Position, Rotation, Scale: TVector4f;
    Root, ID: integer;
    Objects: array of TGLObject;
    Frames: array of array of TGLTransform;
    FrameTimes: array of Single;
    StartFrame, EndFrame: integer;

    Time: Double;

    property Visible: boolean read FVisible write SetVisible;

    function AddObject(OType: TGLObjectType; const Parent: integer = -1): integer;
    procedure SetFramesCount(Count: integer);

    function AddTransform(FrameID, ObjID: integer; Position, Rotation, Scale: TVector4f): integer; overload;
    function AddTransform(FrameID: integer; Transform: TGLTransform): integer; overload;

    function AddFrame(Transforms: array of TGLTransform): integer;

    procedure Animate(DeltaTime: Double);

    function FrameByTime(Time: Double): integer;

    procedure Reset;
    constructor Create;
  end;

implementation

uses uPoAObjects;

constructor TPOAObject.Create;
begin
  inherited;
  SetLength(Objects,0);
  SetLength(Frames,0,0);
  SetLength(FrameTimes,0);
  Scale[0]:=1;
  Scale[1]:=1;
  Scale[2]:=1;

  ID:=length(POAS)-1;
  Root:=call('glObjectAdd', [otRoot, 'poa'+inttostr(ID)+'_root']);
end;

procedure TPOAObject.Reset;
var
  x, y: integer;
  A: array of variant;
begin
  Call('glPosition', [Root, Position[0], Position[1], Position[2]]);
  Call('glRotation', [Root, Rotation[0], Rotation[1], Rotation[2]]);
  Call('glScale', [Root, Scale[0], Scale[1], Scale[2]]);

  for x := 0 to length(Objects) - 1 do
  begin
    Call('glPosition', [Objects[x].ID, Objects[x].Position[0],Objects[x].Position[1],Objects[x].Position[2]]);
    Call('glRotation', [Objects[x].ID, Objects[x].Rotation[0],Objects[x].Rotation[1],Objects[x].Rotation[2]]);
    Call('glScale', [Objects[x].ID, Objects[x].Scale[0],Objects[x].Scale[1],Objects[x].Scale[2]]);

    if length(Objects[x].Params)>0 then
    begin
      SetLength(A, length(Objects[x].Params)+1);
      A[0]:=Objects[x].ID;
      for y := 1 to length(Objects[x].Params) do A[y]:=Objects[x].Params[y-1];
      Call('glObjectParams', A);
    end;
  end;

  Time:=0;
end;

function TPOAObject.FrameByTime(Time: Double): integer;
var
  I: Integer;
  D: Double;
begin
  D:=0;
  Result:=length(Frames);
  for I := Startframe to EndFrame do
  begin
    D:=D+FrameTimes[i];
    if D>Time then
    begin
      Result:=I;
      break;
    end;
  end;
end;

procedure TPOAObject.SetFramesCount(Count: integer);
var
  L: integer;
  I: Integer;
begin
  L:=length(Frames);
  SetLength(Frames, Count);
  SetLength(FrameTimes, Count);
  if L<Count then
    for I := l-1 to Count - 1 do
      FrameTimes[i]:=1;
end;

function TPOAObject.AddTransform(FrameID, ObjID: integer; Position, Rotation, Scale: TVector4f): integer;
begin
  if (FrameID>=0) and (FrameID<length(Frames)) then
  begin
    SetLength(Frames[FrameID], length(Frames[FrameID])+1);
    Frames[FrameID,length(Frames[FrameID])-1].ObjID:=ObjID;
    Frames[FrameID,length(Frames[FrameID])-1].Position:=Position;
    Frames[FrameID,length(Frames[FrameID])-1].Rotation:=Rotation;
    Frames[FrameID,length(Frames[FrameID])-1].Scale:=Scale;
    Result:=length(Frames[FrameID])-1;
  end else Result:=-1;
end;

function TPOAObject.AddTransform(FrameID: integer; Transform: TGLTransform): integer;
begin
  if (FrameID>=0) and (FrameID<length(Frames)) then
  begin
    SetLength(Frames[FrameID], length(Frames[FrameID])+1);
    Frames[FrameID,length(Frames[FrameID])-1]:=Transform;
    Result:=length(Frames[FrameID])-1;
  end else Result:=-1;
end;

function TPOAObject.AddFrame(Transforms: array of TGLTransform): integer;
var
  x: integer;
begin
  SetLength(Frames, length(Frames)+1);
  for x := 0 to length(Transforms) - 1 do
    AddTransform(length(Frames)-1, Transforms[x]);
  Result:=length(Frames)-1;
  SetLength(FrameTimes, length(FrameTimes)+1);
  FrameTimes[length(FrameTimes)-1]:=1;
end;

function TPOAObject.AddObject(OType: TGLObjectType; const Parent: integer = -1): integer;
begin
  SetLength(Objects, length(Objects)+1);
  Objects[length(Objects)-1].ID:=Call('glObjectAdd', [OType, 'poa'+inttostr(ID)+'_'+inttostr(length(Objects)-1)]);
  Objects[length(Objects)-1].ObjType:=OType;
  Objects[length(Objects)-1].Scale[0]:=1;
  Objects[length(Objects)-1].Scale[1]:=1;
  Objects[length(Objects)-1].Scale[2]:=1;
  Result:=length(Objects)-1;

  Call('glObjectParent', [Objects[length(Objects)-1].ID, Root]);
end;

procedure TPOAObject.Animate(DeltaTime: Double);
var
  x, nextFrame, lastFrame: Integer;
  AnimTime: Double;
begin
  if Length(Frames)<=0 then exit;

  Time:=Time+DeltaTime;
  AnimTime:=0;
  for x := StartFrame to EndFrame do
    AnimTime:=AnimTime+FrameTimes[x];

  if Time>=AnimTime then Time:=0;

  nextFrame:=FrameByTime(Time)+1; if nextFrame>EndFrame then nextFrame:=StartFrame;
  lastFrame:=nextFrame-1; if lastFrame<StartFrame then lastFrame:=EndFrame;
 
  for x := 0 to length(Frames[nextFrame]) - 1 do
  begin
    Call('glPosition', [Objects[Frames[nextFrame,x].ObjID].ID,
        Objects[Frames[nextFrame,x].ObjID].Position[0]+Frames[lastFrame,x].Position[0]+(Frames[nextFrame,x].Position[0]-Frames[lastFrame,x].Position[0])*(Time-trunc(Time)),
        Objects[Frames[nextFrame,x].ObjID].Position[1]+Frames[lastFrame,x].Position[1]+(Frames[nextFrame,x].Position[1]-Frames[lastFrame,x].Position[1])*(Time-trunc(Time)),
        Objects[Frames[nextFrame,x].ObjID].Position[2]+Frames[lastFrame,x].Position[2]+(Frames[nextFrame,x].Position[2]-Frames[lastFrame,x].Position[2])*(Time-trunc(Time))]);
    Call('glRotation', [Objects[Frames[nextFrame,x].ObjID].ID,
        Objects[Frames[nextFrame,x].ObjID].Rotation[0]+Frames[lastFrame,x].Rotation[0]+(Frames[nextFrame,x].Rotation[0]-Frames[lastFrame,x].Rotation[0])*(Time-trunc(Time)),
        Objects[Frames[nextFrame,x].ObjID].Rotation[1]+Frames[lastFrame,x].Rotation[1]+(Frames[nextFrame,x].Rotation[1]-Frames[lastFrame,x].Rotation[1])*(Time-trunc(Time)),
        Objects[Frames[nextFrame,x].ObjID].Rotation[2]+Frames[lastFrame,x].Rotation[2]+(Frames[nextFrame,x].Rotation[2]-Frames[lastFrame,x].Rotation[2])*(Time-trunc(Time))]);
    Call('glScale', [Objects[Frames[nextFrame,x].ObjID].ID,
        Scale[0]*Objects[Frames[nextFrame,x].ObjID].Scale[0]*Frames[lastFrame,x].Scale[0]+(Frames[nextFrame,x].Scale[0]-Frames[lastFrame,x].Scale[0])*(Time-trunc(Time)),
        Scale[1]*Objects[Frames[nextFrame,x].ObjID].Scale[1]*Frames[lastFrame,x].Scale[1]+(Frames[nextFrame,x].Scale[1]-Frames[lastFrame,x].Scale[1])*(Time-trunc(Time)),
        Scale[2]*Objects[Frames[nextFrame,x].ObjID].Scale[2]*Frames[lastFrame,x].Scale[2]+(Frames[nextFrame,x].Scale[2]-Frames[lastFrame,x].Scale[2])*(Time-trunc(Time))]);
  end;
end;

procedure TPOAObject.SetVisible(Visible: boolean);
var
  x: Integer;
begin
  FVisible:=Visible;

  for x := 0 to length(objects) - 1 do
    call('glObjectVisible', [objects[x].ID, FVisible]);
end;

end.
