unit Animationrig;

interface

uses SysUtils,Classes,Contnrs,StrUtils,math,
     AnimationrigMisc;

type
  // foward defined
  TAnimrig = class;

  // key
  TAnimrigKey = class
    protected
      fLayerType : TAnimrigMisc.LayerType;
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; virtual; abstract;
    public
      Frame : integer;   // AnimationSet has base frame rate.
      PrevTrackAlpha : single;
      CurrentTrackAlpha : single;
      TrackAlphaEasing : TAnimrigMisc.Easing;
      TrackAlphaPower : single;

      IsPrevKey : boolean;

      const CommonSaveVersion = 2;

      class function CreateKeyFromLayerType(layertype : TAnimrigMisc.LayerType; frame:integer) : TAnimrigKey;
      class function CreateKeyFromBinary(ms:TMemoryStream; const head:string; layertype : TAnimrigMisc.LayerType) : TAnimrigKey;

      constructor Create;
      procedure Clear; virtual;
      function LoadFromBinary(ms:TmemoryStream):boolean;

      property LayerType : TAnimrigMisc.LayerType read fLayerType;
    end;
  TAnimrigKeyHasTransform = class(TAnimrigKey)
    public
      Position : TAnimrigMisc.Vector3;
      Scale : TAnimrigMisc.Vector3;
      Rotate : TAnimrigMisc.Vector3;
      RotateOrder : TAnimrigMisc.RotateOrder;

      TransSpline : TAnimrigMisc.Spline;
      TransformEasing : TAnimrigMisc.Easing;
      TransformPower : single;

      procedure Clear; override;
  end;
  TAnimrigAnimBlend4Key = class(TAnimrigKey)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      Animation : string;
      AnimationAllowAlways : boolean;
      AnimationAdd : boolean;
      AnimationMixBlend : TAnimrigMisc.MixBlend;
      AnimationLoop : boolean;
      AnimationMixDuration : single;
      AnimationLoopBegin : single;
      AnimationLoopEnd : single;

      BlendAlphaX,BlendAlphaY : single;
      BlendAlphaSpline : TAnimrigMisc.Spline;
      BlendAlphaEasing : TAnimrigMisc.Easing;
      BlendAlphaPower : single;

      const ClassSaveVersion = 3;

      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigAnimBlend1Key = class(TAnimrigAnimBlend4Key)
    public
      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigAnimBlend2Key = class(TAnimrigAnimBlend4Key)
    public
      constructor Create;
  end;
  TAnimrigSeekBlend4Key = class(TAnimrigKey)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      Animation : string;
      AnimationAllowAlways : boolean;
      AnimationAdd : boolean;
      AnimationMixBlend : TAnimrigMisc.MixBlend;
      AnimationLoop : boolean;
      AnimationMixDuration : single;
      AnimationLoopBegin : single;
      AnimationLoopEnd : single;

      SeekTimeX,SeekTimeY : single;
      SeekTimeSpline : TAnimrigMisc.Spline;
      SeekTimeEasing : TAnimrigMisc.Easing;
      SeekTimePower : single;

      const ClassSaveVersion = 3;

      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigSeekBlend1Key = class(TAnimrigSeekBlend4Key)
    public
      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigTransformKey = class(TAnimrigKeyHasTransform)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      MulColor : integer;
      TintColor : integer;
      ColorAlpha : single;
      ColorEasing : TAnimrigMisc.Easing;
      ColorPower : single;

      const ClassSaveVersion = 2;

      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigEventKey = class(TAnimrigKey)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      ID : string;
      EventString : array[0..1] of string;
      EventFloat : array[0..1] of single;
      AllowAlways : boolean;     // ID compare
      IgnitionAlways : boolean;

      const ClassSaveVersion = 1;

      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigPriorityKey = class(TAnimrigKey)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      Priority : single;
      PriorityEasing : TAnimrigMisc.Easing;
      PriorityPower : single;

      const ClassSaveVersion = 1;

      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigStateKey = class(TAnimrigKey)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      Enable : boolean;      // enable = object.enable & key.enable
      Visible : boolean;

      const ClassSaveVersion = 1;

      constructor Create;
      procedure Clear; override;
  end;
  TAnimrigPhysicsForceKey = class(TAnimrigKey)
    protected
      function LoadFromBinarySubClass(ms:TmemoryStream):boolean; override;
    public
      ObjectTarget : string;
      PhysicsTarget : string;
      ForceVector : TAnimrigMisc.Vector3;
      ForceEasing : TAnimrigMisc.Easing;
      ForcePower : single;
      ForceSpline : TAnimrigMisc.Spline;

      WiggleRatio : single;
      WiggleCycle : single;

      const ClassSaveVersion = 2;

      constructor Create;
      procedure Clear; override;
  end;

  // layer
  TAnimrigLayer = class
    private
      function GetKeyCount: integer; inline;
      function GetKeys(n: integer): TAnimrigKey; inline;
    protected
      fLayerType : TAnimrigMisc.LayerType;
      fKeyList : TObjectList;
      fPrevKey : TAnimrigKey;

      procedure CreateSetup;
    public
      Name : string;
      Enable : boolean;

      const CommonSaveVersion = 1;

      class function CreateFromLayerType(layertype : TAnimrigMisc.LayerType) : TAnimrigLayer;
      class function CreateFromBinary(ms:TmemoryStream; const head:string) : TAnimrigLayer;

      constructor Create(layertype_:TAnimrigMisc.LayerType);
      destructor Destroy; override;
      procedure Clear; virtual;

      function LoadFromBinary(ms:TmemoryStream):boolean;
      function LoadReserveFromBinary(ms:TmemoryStream):boolean;

      function GetKeyIndex(frame:integer):integer;

      property KeyList : TObjectList read fKeyList;
      property KeyCount : integer read GetKeyCount;
      property Keys[n:integer] : TAnimrigKey read GetKeys;
      property PrevKey : TAnimrigKey read fPrevKey;

      property LayerType : TAnimrigMisc.LayerType read fLayerType;
  end;
  // object
  TAnimrigObject = class
    private
      function GetLayerCount: integer; inline;
      function GetLayers(n: integer): TAnimrigLayer; inline;
    protected
      fObjectType : TAnimrigMisc.ObjectType;
      fLayerList : TObjectList;
      fInputPanelField : TStringList;
      fSettingField : TStringList;

      procedure CreateSetup;
      function  LoadReserveFromBinarySubClass(ms:TMemoryStream):boolean; virtual;
    public
      Name : string;
      AliasName : string;
      Enable : boolean;
      Visible : boolean;
      Offset : TAnimrigMisc.Vector3;
      Scale : TAnimrigMisc.Vector3;
      ParentObjectIndex : integer; // -1 : none
      ParentBoneName : string;
      DrawPriority : single;

      const CommonSaveVersion = 3;
      const ReserveSaveVersion = 7;

      class function CreateFromObjectType(objecttype : TAnimrigMisc.ObjectType) : TAnimrigObject;
      class function CreateFromBinary(ms:TmemoryStream; const head:string) : TAnimrigObject;

      destructor Destroy; override;
      procedure Clear(keeplayer:boolean=true); virtual;

      function LoadFromBinary(ms:TMemoryStream):boolean;
      function LoadReserveFromBinary(ms:TMemoryStream):boolean;

      // object
      property ObjectType : TAnimrigMisc.ObjectType read fObjectType;
      property SettingField : TStringList read fSettingField;
      // layer
      property LayerList : TObjectList read fLayerList;
      property LayerCount : integer read GetLayerCount;
      property Layers[n:integer] : TAnimrigLayer read GetLayers;

      property InputPanelField : TStringList read fInputPanelField;
  end;
  TAnimrigSpineObject = class(TAnimrigObject)
    protected
      function LoadReserveFromBinarySubClass(ms:TMemoryStream):boolean; override;
    public
      SpineFilename : string;
      MixDuration : single;
      PreMultiplyAlpha : boolean;
      BaseScale : single;  // unused
      DefaultSkin : string;

      const ClassSaveVersion = 3;

      constructor Create;
  end;
  TAnimrigNullObject = class(TAnimrigObject)
    protected
      function LoadReserveFromBinarySubClass(ms:TMemoryStream):boolean; override;
    public
      ImageFilename : string;

      const ClassSaveVersion = 1;

      constructor Create;
  end;
  TAnimrigCameraObject = class(TAnimrigObject)
    protected
      function LoadReserveFromBinarySubClass(ms:TMemoryStream):boolean; override;
    public
      RotateOffset : TAnimrigMisc.Vector3;
      AreaBegin : single;
      AreaEnd : single;

      const ClassSaveVersion = 2;

      constructor Create;
  end;

  // animationset setdata
  TAnimrigAnimation = class
    private
      function GetObjectCount: integer; inline;
      function GetObjects(n: integer): TAnimrigObject; inline;
    protected
      fObjectList : TObjectList;
      fLoopBeginFrame : integer;
      fLoopEndFrame : integer;
    public
      Name : string;
      PreviewBaseAnimation : string;
      LoopEnable : boolean;

      const CommonSaveVersion = 1;

      constructor Create;
      destructor Destroy; override;
      procedure Clear(keepobject:boolean=true);

      function GetLoopedFrame(frame:double):double;

      function LoadFromBinary(ms:TMemoryStream) : boolean;

      property LoopBeginFrame : integer read fLoopBeginFrame; // write SetLoopBeginFrame;
      property LoopEndFrame : integer read fLoopEndFrame; // write SetLoopEndFrame;
      // object
      property ObjectList : TObjectList read fObjectList;
      property ObjectCount : integer read GetObjectCount;
      property Objects[n:integer] : TAnimrigObject read GetObjects;
  end;

  // animationset
  TAnimrig = class
    private
      function GetAnimationCount: integer; inline;
      function GetAnimations(n: integer): TAnimrigAnimation; inline;
      function GetObjectCount: integer; inline;
      function GetObjects(n: integer): TAnimrigObject; inline;
    protected
      fAnimationList : TObjectList;
      fObjectList : TObjectList;
      fFramerate : integer;
      fUserMemo : TStringList;
      fLoadVersion : integer;
      fLastError : string;

      const GlobalChunkSaveVersion = 2;
      const CommonSaveVersion = 7;
    public
      Offset : TAnimrigMisc.Vector3;
      Size : TAnimrigMisc.Vector3;

      const SaveVersion =
        TAnimrigKey.CommonSaveVersion +                  // keys
        TAnimrigAnimBlend4Key.ClassSaveVersion +
        TAnimrigSeekBlend4Key.ClassSaveVersion +
        TAnimrigTransformKey.ClassSaveVersion +
        TAnimrigEventKey.ClassSaveVersion +
        TAnimrigPriorityKey.ClassSaveVersion +
        TAnimrigStateKey.ClassSaveVersion +
        TAnimrigPhysicsForceKey.ClassSaveVersion +
        TAnimrigLayer.CommonSaveVersion +                // layer
        TAnimrigObject.CommonSaveVersion + TAnimrigObject.ReserveSaveVersion + // object
        TAnimrigSpineObject.ClassSaveVersion +
        TAnimrigNullObject.ClassSaveVersion +
        TAnimrigCameraObject.ClassSaveVersion +
        TAnimrigAnimation.CommonSaveVersion +            // animation
        CommonSaveVersion +                        // common change version
        GlobalChunkSaveVersion;                    // self

      constructor Create;
      destructor Destroy; override;
      procedure Clear(keepobject:boolean=true);
      function  LoadFromBinary(ms:TMemoryStream) : boolean;

      // frame
      property Framerate : integer read fFramerate; // write SetFramerate;
      // object
      property ObjectList : TObjectList read fObjectList;
      property ObjectCount : integer read GetObjectCount;
      property Objects[n:integer] : TAnimrigObject read GetObjects;
      // data
      function Search(const animationname:string):integer;
      property AnimationList : TObjectList read fAnimationList;
      property AnimationCount : integer read GetAnimationCount;
      property Animations[n:integer] : TAnimrigAnimation read GetAnimations;

      property LoadVersion : integer read fLoadVersion;
      property UserMemo : TStringList read fUserMemo;
      property LastError : string read fLastError;
  end;

implementation

{ TAnimrig }

procedure TAnimrig.Clear(keepobject:boolean);
begin
  fAnimationList.Clear;
  fUserMemo.Clear;

  if not(keepobject) then
  begin
    fObjectList.Clear;
    fFramerate := 30;
    TAnimrigMisc.v3SetVector(Offset,0.0,0.0,0.0);
    TAnimrigMisc.v3SetVector(Size,0.0,0.0,0.0);
  end;

  fLoadVersion := SaveVersion;
end;

constructor TAnimrig.Create;
begin
  fObjectList := TObjectList.Create;
  fAnimationList := TObjectList.Create;
  fUserMemo := TStringList.Create;
  fFramerate := 30;

  Clear;
end;

destructor TAnimrig.Destroy;
begin
  fObjectList.Free;
  fAnimationList.Free;
  fUserMemo.Free;

  inherited;
end;

function TAnimrig.GetAnimationCount: integer;
begin
  result := fAnimationList.Count;
end;

function TAnimrig.GetAnimations(n: integer): TAnimrigAnimation;
begin
  result := TAnimrigAnimation(fAnimationList.Items[n]);
end;

function TAnimrig.GetObjectCount: integer;
begin
  result := fObjectList.Count;
end;

function TAnimrig.GetObjects(n: integer): TAnimrigObject ;
begin
  result := TAnimrigObject(fObjectList.Items[n]);
end;

function TAnimrig.LoadFromBinary(ms: TMemoryStream): boolean;
var
  i : integer;
  c : array [0..7] of ansichar;
  ver,loadver : integer;
  n : integer;
  obj : TAnimrigObject;
  anim : TAnimrigAnimation;
begin
  fLastError := '';
  result := false;

  // head
  try
    for i:=0 to 7 do
      ms.Read(c[i],1);
    
    if not(c[0] = 'a') then raise Exception.Create('');
    if not(c[1] = 'n') then raise Exception.Create('');
    if not(c[2] = 'i') then raise Exception.Create('');
    if not(c[3] = 'm') then raise Exception.Create('');
    if not(c[4] = 'r') then raise Exception.Create('');
    if not(c[5] = 'i') then raise Exception.Create('');
    if not(c[6] = 'g') then raise Exception.Create('');
    if not(c[7] = #0) then raise Exception.Create('');
  except
    fLastError := 'Header error : char code error.';
    exit;
  end;
  
  // version
  try
    loadver := TAnimrigMisc.ReadInteger(ms);
    if (loadver < 0 ) or (loadver > SaveVersion) then
    begin
      fLastError := 'Load version error : ' + IntToStr(loadver);
      exit;
    end;
  except
    fLastError := 'Header error : version chank error.';
    exit;
  end;

  // clear
  Clear(false);
  
  // global
  try
    TAnimrigMisc.ReadHead(ms,'global');
    ver := TAnimrigMisc.ReadInteger(ms,0,GlobalChunkSaveVersion);
    fFramerate := TAnimrigMisc.ReadInteger(ms,5,120,true);
    Offset.x := TAnimrigMisc.ReadFloat(ms,-$ffff,+$ffff,true);
    Offset.y := TAnimrigMisc.ReadFloat(ms,-$ffff,+$ffff,true);
    Offset.z := TAnimrigMisc.ReadFloat(ms,-$ffff,+$ffff,true);
    Size.x := TAnimrigMisc.ReadFloat(ms,0,+8192,true);
    Size.y := TAnimrigMisc.ReadFloat(ms,0,+8192,true);
    Size.z := TAnimrigMisc.ReadFloat(ms,0,+8192,true);
    if ver >= 2 then TAnimrigMisc.ReadStringList(ms,UserMemo);
  except
    fLastError := 'Global Chunk error.';
    exit;
  end;

  // load version
  fLoadVersion := loadver;

  // object
  try
    TAnimrigMisc.ReadHead(ms,'reserveobjects');
    ver := TAnimrigMisc.ReadInteger(ms,0,1);
    n := TAnimrigMisc.ReadInteger(ms,0,255);
  except
    fLastError := 'Reserve chunk error.';
    exit;
  end;

  fObjectList.Clear;
  for i:=0 to n-1 do
  begin
    obj := TAnimrigObject.CreateFromBinary(ms,'reserveobject');
    if obj = nil then
    begin
      fLastError := 'Reserve object head error.';
      exit;
    end;

    fObjectList.Add(obj);
    if not(obj.LoadReserveFromBinary(ms)) then
    begin
      fLastError := 'Reserve object load error.';
      exit;
    end;
  end;

  // animationset sata
  try
    TAnimrigMisc.ReadHead(ms,'animations');
    ver := TAnimrigMisc.ReadInteger(ms,0,1);
    n := TAnimrigMisc.ReadInteger(ms,0,255);
  except
    fLastError := 'Animation chunk data error.';
    exit;
  end;

  fAnimationList.Clear;
  for i:=0 to n-1 do
  begin
    anim := TAnimrigAnimation.Create;
    fAnimationList.Add(anim);
    if not(anim.LoadFromBinary(ms))then
    begin
      fLastError := 'Animation chunk load error.';
      exit;
    end;
  end;

  result := true;
end;

function TAnimrig.Search(const animationname: string): integer;
var
  i : integer;
  ls : string;
begin
  ls := LowerCase(animationname);

  result := -1;
  for i:=0 to ANimationCount-1 do
    if lowercase(Animations[i].Name) = ls then
    begin
      result := i;
      exit;
    end;
end;

{ TAnimrigSet }

procedure TAnimrigAnimation.Clear(keepobject:boolean);
var
  i : integer;
begin
  if keepobject then
  begin
    for i:=0 to ObjectCount-1 do
    begin
      Objects[i].Clear;
    end;
  end
  else
  begin
    fObjectList.Clear;
  end;

  LoopEnable := false;
  fLoopBeginFrame := 0;
  fLoopEndFrame := 0;
end;

constructor TAnimrigAnimation.Create;
begin
  fObjectList := TObjectList.Create;
  Name := TAnimrigMisc._noname;
end;

destructor TAnimrigAnimation.Destroy;
begin
  fObjectList.Free;

  inherited;
end;

function TAnimrigAnimation.GetLoopedFrame(frame: double): double;
var
  len : integer;
begin
  result := frame;
  if not(LoopEnable) then exit;
  if frame < LoopEndFrame then exit;

  len := LoopEndFrame - LoopBeginFrame;
  if len < 1 then exit;

  frame := frame - LoopEndFrame;
  frame := frame - (floor(frame / len)*len);

  result := max(LoopBeginFrame + frame , 0.0);
end;

function TAnimrigAnimation.GetObjectCount: integer;
begin
  result := fObjectList.Count;
end;

function TAnimrigAnimation.GetObjects(n: integer): TAnimrigObject;
begin
  result := TAnimrigObject(fObjectList.Items[n]);
end;

function TAnimrigAnimation.LoadFromBinary(ms: TMemoryStream): boolean;
var
  ver : integer;
  i,n : integer;
  obj : TAnimrigObject;
begin
  Clear(false);

  result := false;
  // fChangeFlag := true;

  try
    TAnimrigMisc.ReadHead(ms,'animation');
    ver := TAnimrigMisc.ReadInteger(ms,0,CommonSaveVersion);
    Name := TAnimrigMisc.ReadString(ms);
    LoopEnable := TAnimrigMisc.ReadBool(ms);
    fLoopBeginFrame := TAnimrigMisc.ReadInteger(ms,0,$ffff);
    fLoopEndFrame := TAnimrigMisc.ReadInteger(ms,0,$ffff);
    PreviewBaseAnimation := TAnimrigMisc.ReadString(ms,256);
  except
    exit;
  end;

  // objects
  try
    n := TAnimrigMisc.ReadInteger(ms,0,255);
  except
    exit;
  end;
  fObjectList.Clear;
  for i:=0 to n-1 do
  begin
    obj := TAnimrigObject.CreateFromBinary(ms,'object');
    if obj = nil then exit;
    fObjectList.Add(obj);
    if not(obj.LoadFromBinary(ms)) then exit;
  end;

  result := true;
end;

{ TAnimrigLayer }

procedure TAnimrigLayer.Clear;
begin
  fKeyList.Clear;
  fPrevKey.Clear;
end;

constructor TAnimrigLayer.Create(layertype_:TAnimrigMisc.LayerType);
begin
  fLayerType := layertype_;
  CreateSetup;
end;

class function TAnimrigLayer.CreateFromBinary(ms: TmemoryStream; const head:string): TAnimrigLayer;
var
  ver : integer;
  t : TAnimrigMisc.LayerType;
  layer : TAnimrigLayer;
begin
  result := nil;
  try
    TAnimrigMisc.ReadHead(ms,head);
    ver := TAnimrigMisc.ReadInteger(ms,CommonSaveVersion);  // ver
    t := TAnimrigMisc.StringToLayerType( TAnimrigMisc.ReadString(ms) );

    layer := TAnimrigLayer.CreateFromLayerType(t);
  except
    exit;
  end;

  try
    layer.name := TAnimrigMisc.ReadString(ms);
    layer.Enable := TAnimrigMisc.ReadBool(ms);
  except
    layer.free;
    exit;
  end;
  result := layer;
end;

class function TAnimrigLayer.CreateFromLayerType(layertype: TAnimrigMisc.LayerType): TAnimrigLayer;
begin
  result := TAnimrigLayer.Create(layertype);
end;

procedure TAnimrigLayer.CreateSetup;
begin
  Name := TAnimrigMisc._noname;
  Enable := true;

  fKeyList := TObjectList.Create;
  fPrevKey := TAnimrigKey.CreateKeyFromLayerType(LayerType,0);
  fPrevKey.IsPrevKey := true;
end;

destructor TAnimrigLayer.Destroy;
begin
  fKeyList.free;
  fPrevKey.free;

  inherited;
end;

function TAnimrigLayer.GetKeyCount: integer;
begin
  result := fKeyList.Count;
end;

function TAnimrigLayer.GetKeyIndex(frame: integer): integer;
var
  i : integer;
begin
  result := -1;

  for i:=KeyCount-1 downto 0 do
    if frame >= Keys[i].Frame then
    begin
      result := i;
      break;
    end;
end;

function TAnimrigLayer.GetKeys(n: integer): TAnimrigKey;
begin
  result := TAnimrigKey(fKeyList.Items[n]);
end;

function TAnimrigLayer.LoadFromBinary(ms: TmemoryStream): boolean;
var
  i,n : integer;
  key : TAnimrigKey;
begin
  result := false;
  // sub class
  //if not LoadFromBinarySubClass(ms) then exit;

  // prevkey
  key := TAnimrigKey.CreateKeyFromBinary(ms,'key',LayerType);
  if (key = nil)then exit;

  if not(key.LoadFromBinary(ms)) then
  begin
    key.free;
    exit;
  end;
  key.IsPrevKey := true;
  fPrevKey.free;
  fPrevKey := key;

  // keys
  try
    n := TAnimrigMisc.ReadInteger(ms);
  except
    exit;
  end;

  fKeyList.Clear;
  for i:=0 to n-1 do
  begin
    key := TAnimrigKey.CreateKeyFromBinary(ms,'key',LayerType);
    if (key = nil)then exit;

    fKeyList.Add(key);
    if not(key.LoadFromBinary(ms)) then exit;
  end;
  result := true;
end;

function TAnimrigLayer.LoadReserveFromBinary(ms: TmemoryStream): boolean;
begin
  // sub class
  // * no data
  // keys
  // * no data
  result := true;
end;

function AnimrigLayer_Compare(o1,o2:pointer):integer;
var
  key1,key2 : TAnimrigKey;
begin
  key1 := TAnimrigKey(o1);
  key2 := TAnimrigKey(o2);
  result := key1.Frame - key2.Frame;
end;

{ TAnimrigObject }

procedure TAnimrigObject.Clear(keeplayer:boolean);
var
  i : integer;
begin
  if keeplayer then
  begin
    for i:=0 to LayerCount-1 do
      Layers[i].Clear;
  end
  else
  begin
    fLayerList.Clear;
    fInputPanelField.Clear;
  end;

  fSettingField.Clear;

  Name := TAnimrigMisc._noname;
  AliasName := '';
  Enable := true;
  Visible := true;
  TAnimrigMisc.v3SetVector(Offset , 0.0,0.0,0.0);
  TAnimrigMisc.v3SetVector(Scale , 1.0,1.0,1.0);
  ParentObjectIndex := -1;
  ParentBoneName := 'none';
  DrawPriority := 0.0;
end;

class function TAnimrigObject.CreateFromBinary(ms: TmemoryStream; const head:string): TAnimrigObject;
var
  ver : integer;
  t : TAnimrigMisc.ObjectType;
  n : string;
  obj : TAnimrigObject;
begin
  result := nil;
  n := '';

  try
    // base head
    TAnimrigMisc.ReadHead(ms,head);
    ver := TAnimrigMisc.ReadInteger(ms,0,CommonSaveVersion);

    t := TAnimrigMisc.StringToObjectType( TAnimrigMisc.ReadString(ms) );
    // name : move to ReserveObject savedata
    if ver < 3 then n := TAnimrigMisc.ReadString(ms);

    obj := TAnimrigObject.CreateFromObjectType(t);
  except
    exit;
  end;

  obj.Name := n;
  result := obj;
end;

class function TAnimrigObject.CreateFromObjectType(objecttype: TAnimrigMisc.ObjectType): TAnimrigObject;
begin
  case objecttype of
    anoSpine : result := TAnimrigSpineObject.Create;
    anoNull : result := TAnimrigNullObject.Create;
    anoCamera : result := TAnimrigCameraObject.Create;
  else
    raise Exception.Create('AddObject : unsupport object : ' + TAnimrigMisc.ObjectTypeToString(objecttype));
  end;
end;

procedure TAnimrigObject.CreateSetup;
begin
  fLayerList := TObjectList.Create;
  fInputPanelField := TStringList.Create;
  fSettingField := TStringList.Create;

  Clear(true);
end;

destructor TAnimrigObject.Destroy;
begin
  fLayerList.Free;

  fInputPanelField.Free;
  fSettingField.Free;
  
  inherited;
end;

function TAnimrigObject.GetLayerCount: integer;
begin
  result := fLayerList.Count;
end;

function TAnimrigObject.GetLayers(n: integer): TAnimrigLayer;
begin
  result := TAnimrigLayer(fLayerList.Items[n]);
end;

function TAnimrigObject.LoadFromBinary(ms: TMemoryStream): boolean;
var
  n,i : integer;
  layer : TAnimrigLayer;
begin
  result := false;
  // sub class
  // * no data
  // layers
  try
    n := TAnimrigMisc.ReadInteger(ms,0,255);
  except
    exit;
  end;

  fLayerList.Clear;
  for i:=0 to n-1 do
  begin
    layer := TAnimrigLayer.CreateFromBinary(ms,'layer');
    if layer = nil then exit;
    fLayerList.Add(layer);
    if not(layer.LoadFromBinary(ms)) then exit;
  end;
  result := true; 
end;

function TAnimrigObject.LoadReserveFromBinary(ms: TMemoryStream): boolean;
var
  n,i : integer;
  layer : TAnimrigLayer;
begin
  result := false;
  // sub class
  if not LoadReserveFromBinarySubClass(ms) then exit;
  // layers
  try
    n := TAnimrigMisc.ReadInteger(ms,0,255);
  except
    exit;
  end;

  fLayerList.Clear;
  for i:=0 to n-1 do
  begin
    layer := TAnimrigLayer.CreateFromBinary(ms,'reservelayer');
    if layer = nil then exit;
    fLayerList.Add(layer);
    if not(layer.LoadReserveFromBinary(ms)) then exit;
  end;
  result := true;
end;

function TAnimrigObject.LoadReserveFromBinarySubClass(ms: TMemoryStream): boolean;
var
  n,i : integer;
  ver : integer;
begin
  result := false;

  try
    // version
    ver := TAnimrigMisc.ReadInteger(ms , 0,ReserveSaveVersion);
    // name
    if ver >= 7 then Name := TAnimrigMisc.ReadString(ms);
    if ver >= 7 then AliasName := TAnimrigMisc.ReadString(ms);
    // state
    Enable := TAnimrigMisc.ReadBool(ms);
    Visible := TAnimrigMisc.ReadBool(ms);
    // offset / scale
    if ver >= 2 then
    begin
      Offset.x := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true);
      Offset.y := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true);
      Offset.z := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true);
      Scale.x := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true);
      Scale.y := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true);
      Scale.z := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true);
    end;
    if ver >= 3 then ParentObjectIndex := TAnimrigMisc.ReadInteger(ms , -1,$ff,true);
    if ver >= 6 then ParentBoneName := TAnimrigMisc.ReadString(ms);
    if ver >= 4 then DrawPriority := TAnimrigMisc.ReadFloat(ms , -$ffff,$ffff,true);
    // setting memo
    if ver >= 5 then TAnimrigMisc.ReadStringList(ms , fSettingField);
    // input field
    fInputPanelField.Clear;
    n := TAnimrigMisc.ReadInteger(ms,0,256);
    for i:=0 to n-1 do
      fInputPanelField.Add( TAnimrigMisc.ReadString(ms) );
  except
    exit;
  end;

  result := true;
end;

{ TAnimrigSpineKey }

procedure TAnimrigAnimBlend4Key.Clear;
begin
  inherited;

  Animation := 'none';
  AnimationAllowAlways := true;
  AnimationAdd := false;
  AnimationMixBlend := anmAdd;
  AnimationMixDuration := -1.0;
  AnimationLoopBegin := 0.0;
  AnimationLoopEnd := 0.0;

  BlendAlphaX := 0.0;
  BlendAlphaY := 0.0;
  BlendAlphaSpline := ansLinear;
  BlendAlphaEasing := aneLinear;
  BlendAlphaPower := 1.0;
end;

constructor TAnimrigAnimBlend4Key.Create;
begin
  inherited;

  fLayerType := anlAnimBlend4;
  Clear;
end;

function TAnimrigAnimBlend4Key.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'animblend4key');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    Animation := TAnimrigMisc.ReadString(ms);
    AnimationAllowAlways := TAnimrigMisc.ReadBool(ms);
    AnimationAdd := TAnimrigMisc.ReadBool(ms);
    AnimationMixBlend := TAnimrigMisc.MixBlend( TAnimrigMisc.ReadInteger(ms,0,1) );
    AnimationLoop := TAnimrigMisc.ReadBool(ms);
    if ver >= 2 then AnimationMixDuration := TAnimrigMisc.ReadFloat(ms , -1,100,true);
    if ver >= 3 then AnimationLoopBegin := TAnimrigMisc.ReadFloat(ms , 0,1000,true);
    if ver >= 3 then AnimationLoopEnd := TAnimrigMisc.ReadFloat(ms , 0,1000,true);

    BlendAlphaX := TAnimrigMisc.ReadFloat(ms , -4.0,+4.0,true,true);
    BlendAlphaY := TAnimrigMisc.ReadFloat(ms , -4.0,+4.0,true,true);
    BlendAlphaSpline := TAnimrigMisc.ReadSpline(ms);
    BlendAlphaEasing := TAnimrigMisc.ReadEasing(ms);
    BlendAlphaPower := TAnimrigMisc.ReadFloat(ms , 0.0,2.0,true,true);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigKey }

procedure TAnimrigKey.Clear;
begin
  // DeleteFlag := false;

  PrevTrackAlpha := 1.0;
  CurrentTrackAlpha := 1.0;
  TrackAlphaEasing := aneLinear;
  TrackAlphaPower := 1.0;
end;

constructor TAnimrigKey.Create;
begin
  fLayerType := anlNone;

  // DeleteFlag := false;
  Frame := 0;
end;

class function TAnimrigKey.CreateKeyFromBinary(ms: TMemoryStream; const head:string; layertype:TAnimrigMisc.LayerType): TAnimrigKey;
var
  ver : integer;
  f : Integer;
  key : TAnimrigKey;
begin
  result := nil;
  try
    TAnimrigMisc.ReadHead(ms,head);
    ver := TAnimrigMisc.ReadInteger(ms,0,CommonSaveVersion); // ver
    // TAnimrigMisc.WriteString(ms,LayerType);  // * use parent layer
    f := TAnimrigMisc.ReadInteger(ms,0,$ffffff);

    key := TAnimrigKey.CreateKeyFromLayerType(layertype,f);
  except
    exit;
  end;

  try
    key.PrevTrackAlpha := TAnimrigMisc.ReadFloat(ms,0.0,2.0,true,true);
    key.CurrentTrackAlpha := TAnimrigMisc.ReadFloat(ms,0.0,2.0,true,true);
    if ver >= 2 then
    begin
      key.TrackAlphaEasing := TAnimrigMisc.ReadEasing(ms);
      key.TrackAlphaPower := TAnimrigMisc.ReadFloat(ms,0.0,2.0,true);
    end;
  except
    key.free;
    exit;
  end;
  result := key;
end;

class function TAnimrigKey.CreateKeyFromLayerType(layertype: TAnimrigMisc.LayerType; frame:integer): TAnimrigKey;
begin
  case layertype of
    anlAnimBlend4 : result := TAnimrigAnimBlend4Key.Create;
    anlAnimBlend2 : result := TAnimrigAnimBlend2Key.Create;
    anlAnimBlend1 : result := TAnimrigAnimBlend1Key.Create;
    anlSeekBlend4 : result := TAnimrigSeekBlend4Key.Create;
    anlSeekBlend1 : result := TAnimrigSeekBlend1Key.Create;
    anlTransform : result := TAnimrigTransformKey.Create;
    anlEvent     : result := TAnimrigEventKey.Create;
    anlPriority  : result := TAnimrigPriorityKey.Create;
    anlState     : result := TAnimrigStateKey.Create;
    anlPhysicsForce : result := TAnimrigPhysicsForceKey.Create;
    else
      raise Exception.Create('CreateKeyFromLayerType : unsupport type.');
  end;

  if result <> nil then
  begin
    result.Frame := frame;
  end;
end;

function TAnimrigKey.LoadFromBinary(ms: TmemoryStream): boolean;
begin
  result := false;
  // sub class
  if not(LoadFromBinarySubClass(ms)) then exit;
  // end of data
  result := true;
end;

{ TAnimrigKeyCanTransform }

procedure TAnimrigKeyHasTransform.Clear;
begin
  inherited;

  TAnimrigMisc.v3SetVector(Position , 0.0,0.0,0.0);
  TAnimrigMisc.v3SetVector(Scale , 1.0,1.0,1.0);
  TAnimrigMisc.v3SetVector(Rotate , 0.0,0.0,0.0);

  RotateOrder := anrPass;

  TransSpline := ansLinear;
  TransformEasing := aneLinear;
  TransformPower := 1.0;
end;

{ TAnimrigSpineObject }

constructor TAnimrigSpineObject.Create;
begin
  inherited;

  fObjectType := anoSpine;
  CreateSetup;

  SpineFilename := '';
  MixDuration := 0.25;
  BaseScale := 1.0;
  PreMultiplyAlpha := false;
  DefaultSkin := 'default';
end;

function TAnimrigSpineObject.LoadReserveFromBinarySubClass(ms: TMemoryStream): boolean;
var
  ver : integer;
begin
  result := false;

  // inherited
  if not(inherited LoadReserveFromBinarySubClass(ms)) then exit;

  try
    TAnimrigMisc.ReadHead(ms,'spineobject');
    ver := TAnimrigMisc.ReadInteger(ms,0,ClassSaveVersion);
    //SpineFilename := ReplaceStr(TAnimrigMisc.ReadString(ms),'/','\');      // / -> \
    SpineFilename := TAnimrigMisc.ReadString(ms);
    MixDuration := TAnimrigMisc.ReadFloat(ms,0,$ffff);
    BaseScale := TAnimrigMisc.ReadFloat(ms,0,$ffff);
    if ver >= 2 then PreMultiplyAlpha := TAnimrigMisc.ReadBool(ms);
    if ver >= 3 then DefaultSkin := TAnimrigMisc.ReadString(ms);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigTransformKey }

procedure TAnimrigTransformKey.Clear;
begin
  inherited;

  MulColor := $ffffff;
  ColorAlpha := 1.0;
  TintColor := $000000;
  ColorEasing := aneLinear;
  ColorPower := 1.0;
end;

constructor TAnimrigTransformKey.Create;
begin
  fLayerType := anlTransform;
  Clear;
end;

function TAnimrigTransformKey.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'transformkey');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    Position.x := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    Position.y := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    Position.z := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);

    Scale.x := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    Scale.y := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    Scale.z := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);

    Rotate.x := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    Rotate.y := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    Rotate.z := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    RotateOrder := TAnimrigMisc.ReadRotateOrder(ms);

    TransSpline := TAnimrigMisc.ReadSpline(ms);
    TransformEasing := TAnimrigMisc.ReadEasing(ms);
    TransformPower := TAnimrigMisc.ReadFloat(ms , 0.0,2.0,true);

    MulColor := TAnimrigMisc.ReadInteger(ms , $000000,$ffffff,true,true);
    ColorAlpha := TAnimrigMisc.ReadFloat(ms , 0,1.0,true,true);
    if ver >= 2 then TintColor := TAnimrigMisc.ReadInteger(ms , $000000,$ffffff,true,true);
    ColorEasing := TAnimrigMisc.ReadEasing(ms);
    ColorPower := TAnimrigMisc.ReadFloat(ms , 0.0,2.0,true);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigEventKey }

procedure TAnimrigEventKey.Clear;
begin
  inherited;

  ID := 'none';
  EventString[0] := '';
  EventString[1] := '';
  EventFloat[0] := 0.0;
  EventFloat[1] := 0.0;

  AllowAlways := true;
  IgnitionAlways := false;
end;

constructor TAnimrigEventKey.Create;
begin
  fLayerType := anlEvent;
  Clear;
end;

function TAnimrigEventKey.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'eventkey');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    ID := TAnimrigMisc.ReadString(ms);
    EventString[0] := TAnimrigMisc.ReadString(ms);
    EventString[1] := TAnimrigMisc.ReadString(ms);
    EventFloat[0] := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    EventFloat[1] := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);

    AllowAlways := TAnimrigMisc.ReadBool(ms);
    IgnitionAlways := TAnimrigMisc.ReadBool(ms);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigAnimBlend1Key }

procedure TAnimrigAnimBlend1Key.Clear;
begin
  inherited;
  AnimationMixBlend := anmReplace;
end;

constructor TAnimrigAnimBlend1Key.Create;
begin
  inherited;

  fLayerType := anlAnimBlend1;
  Clear;
end;

{ TAnimrigNullObject }

constructor TAnimrigNullObject.Create;
begin
  inherited;

  fObjectType := anoNull;
  CreateSetup;

  ImageFilename := '';
end;

function TAnimrigNullObject.LoadReserveFromBinarySubClass(ms: TMemoryStream): boolean;
var
  ver : integer;
begin
  result := false;

  // inherited
  if not(inherited LoadReserveFromBinarySubClass(ms)) then exit;

  try
    TAnimrigMisc.ReadHead(ms,'nullobject');
    ver := TAnimrigMisc.ReadInteger(ms,0,ClassSaveVersion);
    // ImageFilename := ReplaceStr(TAnimrigMisc.ReadString(ms),'/','\');      // / -> \
    ImageFilename := TAnimrigMisc.ReadString(ms);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigSeekBlend4Key }

procedure TAnimrigSeekBlend4Key.Clear;
begin
  inherited;

  Animation := 'none';
  AnimationAllowAlways := true;
  AnimationAdd := false;
  AnimationMixBlend := anmAdd;
  AnimationMixDuration := -1.0;
  AnimationLoopBegin := 0.0;
  AnimationLoopEnd := 0.0;

  SeekTimeX := 0.0;
  SeekTimeY := 0.0;
  SeekTimeSpline := ansLinear;
  SeekTimeEasing := aneLinear;
  SeekTimePower := 1.0;
end;

constructor TAnimrigSeekBlend4Key.Create;
begin
  fLayerType := anlSeekBlend4;
  Clear;
end;

function TAnimrigSeekBlend4Key.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'seekblend4key');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    Animation := TAnimrigMisc.ReadString(ms);
    AnimationAllowAlways := TAnimrigMisc.ReadBool(ms);
    AnimationAdd := TAnimrigMisc.ReadBool(ms);
    AnimationMixBlend := TAnimrigMisc.MixBlend( TAnimrigMisc.ReadInteger(ms,0,1) );
    AnimationLoop := TAnimrigMisc.ReadBool(ms);
    if ver >= 2 then AnimationMixDuration := TAnimrigMisc.ReadFloat(ms , -1,100,true);
    if ver >= 3 then AnimationLoopBegin := TAnimrigMisc.ReadFloat(ms , 0,1000,true);
    if ver >= 3 then AnimationLoopEnd := TAnimrigMisc.ReadFloat(ms , 0,1000,true);

    SeekTimeX := TAnimrigMisc.ReadFloat(ms , -4.0,+4.0,true,true);
    SeekTimeY := TAnimrigMisc.ReadFloat(ms , -4.0,+4.0,true,true);
    SeekTimeSpline := TAnimrigMisc.ReadSpline(ms);
    SeekTimeEasing := TAnimrigMisc.ReadEasing(ms);
    SeekTimePower := TAnimrigMisc.ReadFloat(ms , 0.0,2.0,true);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigSeekBlend1Key }

procedure TAnimrigSeekBlend1Key.Clear;
begin
  inherited;
  AnimationMixBlend := anmReplace;
end;

constructor TAnimrigSeekBlend1Key.Create;
begin
  fLayerType := anlSeekBlend1;
  Clear;
end;

{ TAnimrigPriorityKey }

procedure TAnimrigPriorityKey.Clear;
begin
  inherited;

  Priority := 0.0;
  PriorityEasing := aneNone;
  PriorityPower := 1.0;
end;

constructor TAnimrigPriorityKey.Create;
begin
  fLayerType := anlPriority;
  Clear;
end;

function TAnimrigPriorityKey.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'prioritykey');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    Priority := TAnimrigMisc.ReadFloat(ms , -$ffff,+$ffff,true,true);
    PriorityEasing := TAnimrigMisc.ReadEasing(ms);
    PriorityPower := TAnimrigMisc.ReadFloat(ms , 0.0,1.0,true);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigStateKey }

procedure TAnimrigStateKey.Clear;
begin
  inherited;

  Enable := true;
  Visible := true;
end;

constructor TAnimrigStateKey.Create;
begin
  fLayerType := anlState;
  Clear;
end;

function TAnimrigStateKey.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'statekey');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    Enable := TAnimrigMisc.ReadBool(ms);
    Visible := TAnimrigMisc.ReadBool(ms);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigPhysicsForceKey }

procedure TAnimrigPhysicsForceKey.Clear;
begin
  inherited;

  ObjectTarget := 'self';
  PhysicsTarget := 'none';
  TAnimrigMisc.v3SetVector(ForceVector,0.0,0.0,0.0);
  ForceEasing := aneLinear;
  ForcePower := 1.0;
  ForceSpline := ansLinear;
  WiggleRatio := 0.0;
  WiggleCycle := 5.0;
end;

constructor TAnimrigPhysicsForceKey.Create;
begin
  fLayerType := anlPhysicsForce;
  Clear;
end;

function TAnimrigPhysicsForceKey.LoadFromBinarySubClass(ms: TmemoryStream): boolean;
var
  ver : integer;
begin
  result := false;
  try
    TAnimrigMisc.ReadHead(ms,'physicsforcekey');
    ver := TAnimrigMisc.ReadInteger(ms , 0,ClassSaveVersion); // ver

    if (ver >= 2) then ObjectTarget := TAnimrigMisc.ReadString(ms);
    PhysicsTarget := TAnimrigMisc.ReadString(ms);
    ForceVector.x := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true,true);
    ForceVector.y := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true,true);
    ForceVector.z := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true,true);
    ForceEasing := TAnimrigMisc.ReadEasing(ms);
    ForcePower := TAnimrigMisc.ReadFloat(ms,0.0,2.0,true);
    ForceSpline := TAnimrigMisc.ReadSpline(ms);

    WiggleRatio := TAnimrigMisc.ReadFloat(ms,0.0,1.0,true);
    WiggleCycle := TAnimrigMisc.ReadFloat(ms,0.1,100.0,true);
  except
    exit;
  end;
  result := true;
end;

{ TAnimrigAnimBlend2Key }

constructor TAnimrigAnimBlend2Key.Create;
begin
  inherited;

  fLayerType := anlAnimBlend2;
  Clear;
end;

{ TAnimrigCameraObject }

constructor TAnimrigCameraObject.Create;
begin
  inherited;

  fObjectType := anoCamera;
  CreateSetup;

  TAnimrigMisc.v3SetVector(RotateOffset , 0,0,0);
  AreaBegin := 0.0;
  AreaEnd := 0.0;
end;

function TAnimrigCameraObject.LoadReserveFromBinarySubClass(ms: TMemoryStream): boolean;
var
  ver : integer;
begin
  Result := false;

  // inherited
  if not(inherited LoadReserveFromBinarySubClass(ms)) then exit;

  try
    TAnimrigMisc.ReadHead(ms,'cameraobject');
    ver := TAnimrigMisc.ReadInteger(ms,0,ClassSaveVersion);

    RotateOffset.x := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true);
    RotateOffset.y := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true);
    RotateOffset.z := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true);

    if ver >= 2 then AreaBegin := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true);
    if ver >= 2 then AreaEnd := TAnimrigMisc.ReadFloat(ms,-$ffff,$ffff,true);
  except
    exit;
  end;
  result := true;
end;


end.
