【Spine】Spine Runtime for Delphi移植笔记(七)

2017-08-02 22:40:09来源:cnblogs.com作者:spine.core.skeleton - 水中盗影人点击

分享
//////////////////////////////////////////////////////////////////////////////////Generic delphi runtime v3.6 for Spine animation tool                        ////Runtime port by cjk (hzi1980@163.com)                                       //////////////////////////////////////////////////////////////////////////////////unit spine.core.skeleton;interfaceuses  System.Classes, System.SysUtils, System.Generics.Collections, System.Math,  spine.types, spine.classes, spine.data,  spine.core.bone, spine.core.slot, spine.core.skin, spine.core.attachment, spine.core.constraint;type  TSpineSkeleton = class(ISkeleton)  private    FPathConstraints: TObjectList<TPathConstraint>;    FIkConstraints: TObjectList<TIkConstraint>;    FTransformConstraints: TObjectList<TTransformConstraint>;    FBones: TObjectList<TSpineBone>;    FSlots: TObjectList<TSpineSlot>;    FDrawOrder: TList<TSpineSlot>;    FUpdateCacheList: TList<IUpdateable>;    FData: TSkeletonData;    FUpdateCacheReset: TList<TSpineBone>;    function GetRootBone: TSpineBone;  private    procedure SortIkConstraint(const AConstraint: TIkConstraint);    procedure SortPathConstraint(const AConstraint: TPathConstraint);    procedure SortTransformConstraint(const AConstraint: TTransformConstraint);    procedure SortPathConstraintAttachment(const ASkin: TSpineSkin;      const ASlotIndex: Integer; const ASlotBone: TSpineBone); overload;    procedure SortPathConstraintAttachment(const AAttachment: IAttachment;      const ASlotBone: TSpineBone); overload;    procedure SortBone(const ABone: TSpineBone);    procedure SortReset(const ABones: TObjectList<TSpineBone>);  public    Skin: TSpineSkin;    R, G, B, A: Single;    Time: Single;    X, Y: Single;    FlipX, FlipY: Boolean;    property Data: TSkeletonData read FData;    property Bones: TObjectList<TSpineBone> read FBones;    property UpdateCacheList: TList<IUpdateable> read FUpdateCacheList;    property Slots: TObjectList<TSpineSlot> read FSlots;    property DrawOrder: TList<TSpineSlot> read FDrawOrder;    property IkConstraints: TObjectList<TIkConstraint> read FIkConstraints;    property PathConstraints: TObjectList<TPathConstraint> read FPathConstraints;    property TransformConstraints: TObjectList<TTransformConstraint> read FTransformConstraints;    property RootBone: TSpineBone read GetRootBone;  public    constructor Create(const AData: TSkeletonData);    destructor Destroy; override;    procedure UpdateCache;    procedure UpdateWorldTransform;    procedure SetToSetupPose;    procedure SetBonesToSetupPose;    procedure SetSlotsToSetupPose;    procedure SetSkin(const ASkinName: string); overload;    procedure SetSkin(const ASkin: TSpineSkin); overload;    procedure SetAttachment(const ASlotName, AAttachmentName: string);    procedure Update(const ADelta: Single);    procedure GetBounds(out oX, oY, oWidth, oHeight: Single; var AVertexBuffer: TArray<Single>);    function GetAttachment(const ASlotName, AAttachmentName: string): IAttachment; overload;    function GetAttachment(const ASlotIndex: Integer; const AAttachmentName: string): IAttachment; overload;    function FindBone(const ABoneName: string): TSpineBone;    function FindBoneIndex(const ABoneName: string): Integer;    function FindSlot(const ASlotName: string): TSpineSlot;    function FindSlotIndex(const ASlotName: string): Integer;    function FindIkConstraint(const AConstraintName: string): TIkConstraint;    function FindTransformConstraint(const AConstraintName: string): TTransformConstraint;    function FindPathConstraint(const AConstraintName: string): TPathConstraint;  end;implementation{ TSpineSkeleton }function TSpineSkeleton.GetRootBone: TSpineBone;begin  result:= nil;  if FBones.Count > 0 then    result:= FBones.Items[0];end;constructor TSpineSkeleton.Create(const AData: TSkeletonData);var  lBoneData: TBoneData;  lParentBone, lBone: TSpineBone;  lSlotData: TSlotData;  lSlot: TSpineSlot;  lIkData  : TIkConstraintData;  lTfData  : TTransformConstraintData;  lPathData: TPathConstraintData;begin  inherited Create;  R:= 1; G:= 1; B:= 1; A:= 1;  FUpdateCacheList:= TList<IUpdateable>.Create;  FUpdateCacheReset:= TList<TSpineBone>.Create;  FDrawOrder:= TList<TSpineSlot>.Create;  FBones:= TObjectList<TSpineBone>.Create;  FSlots:= TObjectList<TSpineSlot>.Create;  FIkConstraints:= TObjectList<TIkConstraint>.Create;  FPathConstraints:= TObjectList<TPathConstraint>.Create;  FTransformConstraints:= TObjectList<TTransformConstraint>.Create;  if not Assigned(AData) then raise Exception.Create('data cannot be null.');  FData:= AData;  for lBoneData in FData.BoneDatas do  begin    if not Assigned(lBoneData.Parent) then      FBones.Add(TSpineBone.Create(lBoneData, Self, nil))    else    begin      lParentBone:= FBones.Items[lBoneData.Parent.Index];      lBone:= TSpineBone.Create(lBoneData, Self, lParentBone);      FBones.Add(lBone);    end;  end;  for lSlotData in FData.SlotDatas do  begin    lBone:= FBones.Items[lSlotData.BoneData.Index];    lSlot:= TSpineSlot.Create(lSlotData, lBone);    FSlots.Add(lSlot);    FDrawOrder.Add(lSlot);  end;  for lIkData in FData.IkConstraintDatas do    FIkConstraints.Add(TIkConstraint.Create(lIkData, Self));  for lTfData in FData.TransformConstraintDatas do    FTransformConstraints.Add(TTransformConstraint.Create(lTfData, Self));  for lPathData in FData.PathConstraintDatas do    FPathConstraints.Add(TPathConstraint.Create(lPathData, Self));  //  UpdateCache();  UpdateWorldTransform();end;destructor TSpineSkeleton.Destroy;begin  FUpdateCacheReset.Free;  FBones.Free;  FUpdateCacheList.Free;  FSlots.Free;  FDrawOrder.Free;  FIkConstraints.Free;  FPathConstraints.Free;  FTransformConstraints.Free;  inherited;end;procedure TSpineSkeleton.UpdateCache;var  i, lConstraintCount, j: Integer;  lFound: Boolean;begin  FUpdateCacheList.Clear;  FUpdateCacheReset.Clear;  for i:= 0 to FBones.Count -1 do    FBones.Items[i].Sorted:= False;  lConstraintCount:= FIkConstraints.Count +                     FTransformConstraints.Count +                     FPathConstraints.Count;  lFound:= False;  for i:= 0 to lConstraintCount -1 do  begin    for j:= 0 to FIkConstraints.Count -1 do    begin      if FIkConstraints[j].Data.Order = j then      begin        SortIkConstraint(FIkConstraints[j]);        lFound:= True;        break;      end;    end;    if not lFound then    begin      for j:= 0 to FTransformConstraints.Count -1 do      begin        if FTransformConstraints[j].Data.Order = j then        begin          SortTransformConstraint(FTransformConstraints[j]);          lFound:= True;          break;        end;      end;    end;    if not lFound then    begin      for j:= 0 to FPathConstraints.Count -1 do      begin        if FPathConstraints[j].Data.Order = j then        begin          SortPathConstraint(FPathConstraints[j]);          lFound:= True;          break;        end;      end;    end;  end;  //  for i:= 0 to FBones.Count -1 do    SortBone(FBones.Items[i]);end;procedure TSpineSkeleton.SortIkConstraint(const AConstraint: TIkConstraint);var  lChild: TSpineBone;begin  SortBone(AConstraint.Target);  SortBone(AConstraint.Bones.Items[0]);  if AConstraint.Bones.Count > 1 then  begin    lChild:= AConstraint.Bones.Last;    if not FUpdateCacheList.Contains(lChild) then      FUpdateCacheReset.Add(lChild);  end;  FUpdateCacheList.Add(AConstraint);    SortReset(AConstraint.Bones.Items[0].Children);  AConstraint.Bones.Last.Sorted:= True;end;procedure TSpineSkeleton.SortPathConstraint(const AConstraint: TPathConstraint);var  lSlot: TSpineSlot;  lSlotIndex, i: Integer;  lSlotBone: TSpineBone;begin  lSlot:= AConstraint.Target;  lSlotIndex:= lSlot.Data.Index;  lSlotBone := lSlot.Bone;  if Assigned(Skin) then    SortPathConstraintAttachment(Skin, lSlotIndex, lSlotBone);  if Assigned(FData.DefaultSkin) and not FData.DefaultSkin.Equals(Skin) then    SortPathConstraintAttachment(FData.DefaultSkin, lSlotIndex, lSlotBone);  for i:= 0 to FData.Skins.Count -1 do    SortPathConstraintAttachment(FData.Skins.Items[i], lSlotIndex, lSlotBone);  if (lSlot.Attachment is TPathAttachment) then    SortPathConstraintAttachment(lSlot.Attachment, lSlotBone);  for i:= 0 to AConstraint.Bones.Count -1 do    SortBone(AConstraint.Bones.Items[i]);  //  FUpdateCacheList.Add(AConstraint);  for i:= 0 to AConstraint.Bones.Count -1 do    SortReset(AConstraint.Bones.Items[i].Children);  for i:= 0 to AConstraint.Bones.Count -1 do    AConstraint.Bones.Items[i].Sorted:= True;end;procedure TSpineSkeleton.SortTransformConstraint(const AConstraint: TTransformConstraint);var  i: Integer;  lChild: TSpineBone;begin  SortBone(AConstraint.Target);  if AConstraint.Data.Local then  begin    for i:= 0 to AConstraint.Bones.Count -1 do    begin      lChild:= AConstraint.Bones.Items[i];      SortBone(lChild.Parent);      if not FUpdateCacheList.Contains(lChild) then        FUpdateCacheReset.Add(lChild);    end;  end else  begin    for i:= 0 to AConstraint.Bones.Count -1 do      SortBone(AConstraint.Bones.Items[i]);  end;  FUpdateCacheList.Add(AConstraint);  for i:= 0 to AConstraint.Bones.Count -1 do    SortReset(AConstraint.Bones.Items[i].Children);  for i:= 0 to AConstraint.Bones.Count -1 do    AConstraint.Bones.Items[i].Sorted:= True;end;procedure TSpineSkeleton.SortPathConstraintAttachment(const ASkin: TSpineSkin;  const ASlotIndex: Integer; const ASlotBone: TSpineBone);var  lPair: TPair<TAttachmentKeyTuple,IAttachment>;begin  for lPair in Skin.Attachments do  begin    if lPair.Key.SlotIndex = ASlotIndex then      SortPathConstraintAttachment(lPair.Value, ASlotBone);  end;end;procedure TSpineSkeleton.SortPathConstraintAttachment(  const AAttachment: IAttachment; const ASlotBone: TSpineBone);var  lPathBones: TArray<Integer>;  i, n, nn, boneCount: Integer;begin  if not (AAttachment is TPathAttachment) then exit;  //  lPathBones:= TPathAttachment(AAttachment).Bones;  if Length(lPathBones) = 0 then    SortBone(ASlotBone)  else  begin    i:= 0;    n:= Length(lPathBones);    while i < n do    begin            boneCount:= lPathBones[i + 1];            i:= i + 1;            nn:= i + boneCount;            while i < nn do      begin                SortBone(FBones.Items[lPathBones[i + 1]]);                i:= i + 1;            end    end;  end;end;procedure TSpineSkeleton.SortBone(const ABone: TSpineBone);begin  if ABone.Sorted then exit;  //  if Assigned(ABone.Parent) then    SortBone(ABone.Parent);  ABone.Sorted:= True;  FUpdateCacheList.Add(ABone);end;procedure TSpineSkeleton.SortReset(const ABones: TObjectList<TSpineBone>);var  i: Integer;  lBone: TSpineBone;begin  for i:= 0 to ABones.Count -1 do  begin    lBone:= ABones.Items[i];    if lBone.Sorted then      SortReset(lBone.Children);    lBone.Sorted:= False;  end;end;procedure TSpineSkeleton.UpdateWorldTransform;var  i: Integer;  lBone: TSpineBone;begin  for i:= 0 to FUpdateCacheReset.Count -1 do  begin    lBone:= FUpdateCacheReset.Items[i];    lBone.AppliedX:= lBone.X;    lBone.AppliedY:= lBone.Y;    lBone.AppliedRotation:= lBone.Rotation;    lBone.AppliedScaleX:= lBone.ScaleX;    lBone.AppliedScaleY:= lBone.ScaleY;    lBone.AppliedShearX:= lBone.ShearX;    lBone.AppliedShearY:= lBone.ShearY;    lBone.AppliedValid:= True;  end;  for i:= 0 to FUpdateCacheList.Count -1 do    FUpdateCacheList.Items[i].Update;end;procedure TSpineSkeleton.SetToSetupPose;begin  SetBonesToSetupPose();  SetSlotsToSetupPose();end;procedure TSpineSkeleton.SetBonesToSetupPose;var  i: Integer;begin  for i:= 0 to FBones.Count -1 do    FBones.Items[i].SetToSetupPose;  for i:= 0 to FIkConstraints.Count -1 do  begin    with FIkConstraints.Items[i] do    begin      BendDirection:= Data.BendDirection;      Mix:= Data.Mix;    end;  end;  for i:= 0 to FTransformConstraints.Count -1 do  begin    with FTransformConstraints.Items[i] do    begin      RotateMix:= Data.RotateMix;      TranslateMix:= Data.TranslateMix;      ScaleMix:= Data.ScaleMix;      ShearMix:= Data.ShearMix;    end;  end;  for i:= 0 to FPathConstraints.Count -1 do  begin    with FPathConstraints.Items[i] do    begin      Position:= Data.Position;      Spacing:= Data.Spacing;      RotateMix:= Data.RotateMix;      TranslateMix:= Data.TranslateMix;    end;  end;end;procedure TSpineSkeleton.SetSlotsToSetupPose;var  i: Integer;begin  FDrawOrder.Clear;  for i:= 0 to FSlots.Count -1 do    FDrawOrder.Add(FSlots.Items[i]);  for i:= 0 to FSlots.Count -1 do    FSlots.Items[i].SetToSetupPose;end;function TSpineSkeleton.FindBone(const ABoneName: string): TSpineBone;var  i: Integer;begin  if ABoneName.Trim.IsEmpty then raise Exception.Create('boneName cannot be null.');  for i:= 0 to FBones.Count -1 do  begin    if FBones.Items[i].Data.Name = ABoneName then      exit(FBones.Items[i]);  end;  result:= nil;end;function TSpineSkeleton.FindBoneIndex(const ABoneName: string): Integer;var  i: Integer;begin  result:= -1;  if ABoneName.Trim.IsEmpty then raise Exception.Create('boneName cannot be null.');  for i:= 0 to FBones.Count -1 do  begin    if FBones.Items[i].Data.Name = ABoneName then      exit(i);  end;end;function TSpineSkeleton.FindSlot(const ASlotName: string): TSpineSlot;var  i: Integer;begin  result:= nil;  if ASlotName.Trim.IsEmpty then raise Exception.Create('slotName cannot be null.');  for i:= 0 to FSlots.Count -1 do  begin    if FSlots.Items[i].Data.Name = ASlotName then      exit(FSlots.Items[i]);  end;end;function TSpineSkeleton.FindSlotIndex(const ASlotName: string): Integer;var  i: Integer;begin  result:= -1;  if ASlotName.Trim.IsEmpty then raise Exception.Create('slotName cannot be null.');  for i:= 0 to FSlots.Count -1 do  begin    if FSlots.Items[i].Data.Name = ASlotName then      exit(i);  end;end;procedure TSpineSkeleton.SetSkin(const ASkinName: string);var  lSkin: TSpineSkin;begin  lSkin:= FData.FindSkin(ASkinName);  if not Assigned(lSkin) then raise Exception.CreateFmt('Skin not found: %s',[ASkinName]);    SetSkin(lSkin);end;procedure TSpineSkeleton.SetSkin(const ASkin: TSpineSkin);var  i: Integer;  lAttachmentName: string;  lAttachment: IAttachment;begin  if Assigned(Self.Skin) then    ASkin.AttachAll(Self, Self.Skin)  else  begin    for i:= 0 to FSlots.Count -1 do    begin      lAttachmentName:= FSlots.Items[i].Data.AttachmentName;      if not lAttachmentName.Trim.IsEmpty then      begin        lAttachment:= ASkin.GetAttachment(i, lAttachmentName);        if Assigned(lAttachment) then          FSlots.Items[i].Attachment:= lAttachment;      end;    end;  end;  Self.Skin:= ASkin;end;function TSpineSkeleton.GetAttachment(const ASlotName,  AAttachmentName: string): IAttachment;begin  result:= GetAttachment(FData.FindSlotIndex(ASlotName), AAttachmentName);end;function TSpineSkeleton.GetAttachment(const ASlotIndex: Integer;  const AAttachmentName: string): IAttachment;var  lAttachment: IAttachment;begin  result:= nil;  if AAttachmentName.Trim.IsEmpty then raise Exception.Create('attachmentName cannot be null.');  if Assigned(Self.Skin) then  begin    lAttachment:= Self.Skin.GetAttachment(ASlotIndex, AAttachmentName);    if Assigned(lAttachment) then exit(lAttachment);  end;  if Assigned(FData.DefaultSkin) then    result:= FData.DefaultSkin.GetAttachment(ASlotIndex, AAttachmentName);end;procedure TSpineSkeleton.SetAttachment(const ASlotName,  AAttachmentName: string);var  i: Integer;  lSlot: TSpineSlot;  lAttachment: IAttachment;begin  if ASlotName.Trim.IsEmpty then raise Exception.Create('slotName cannot be null.');  for i:= 0 to FSlots.Count -1 do  begin    lSlot:= FSlots.Items[i];    if lSlot.Data.Name.Equals(ASlotName) then    begin      lAttachment:= nil;      if not AAttachmentName.Trim.IsEmpty then      begin        lAttachment:= Self.GetAttachment(i, AAttachmentName);        if not Assigned(lAttachment) then          raise Exception.CreateFmt('Attachment not found: %s, for slot: %s',[AAttachmentName,ASlotName]);      end;      lSlot.Attachment:= lAttachment;      exit;    end;  end;  raise Exception.CreateFmt('Slot not found: ',[ASlotName]);end;function TSpineSkeleton.FindIkConstraint(  const AConstraintName: string): TIkConstraint;var  i: Integer;  lConstraint: TIkConstraint;begin  result:= nil;  if AConstraintName.Trim.IsEmpty then raise Exception.Create('constraintName cannot be null.');  for i:= 0 to FIkConstraints.Count -1 do  begin    lConstraint:= FIkConstraints.Items[i];    if lConstraint.Data.Name.Equals(AConstraintName) then exit(lConstraint);  end;end;function TSpineSkeleton.FindTransformConstraint(  const AConstraintName: string): TTransformConstraint;var  i: Integer;  lConstraint: TTransformConstraint;begin  result:= nil;  if AConstraintName.Trim.IsEmpty then raise Exception.Create('constraintName cannot be null.');  for i:= 0 to FTransformConstraints.Count -1 do  begin    lConstraint:= FTransformConstraints.Items[i];    if lConstraint.Data.Name.Equals(AConstraintName) then exit(lConstraint);  end;end;function TSpineSkeleton.FindPathConstraint(  const AConstraintName: string): TPathConstraint;var  i: Integer;  lConstraint: TPathConstraint;begin  result:= nil;  if AConstraintName.Trim.IsEmpty then raise Exception.Create('constraintName cannot be null.');  for i:= 0 to FPathConstraints.Count -1 do  begin    lConstraint:= FPathConstraints.Items[i];    if lConstraint.Data.Name.Equals(AConstraintName) then exit(lConstraint);  end;end;procedure TSpineSkeleton.Update(const ADelta: Single);begin  Self.Time:= Self.Time + ADelta;end;procedure TSpineSkeleton.GetBounds(out oX, oY, oWidth, oHeight: Single;  var AVertexBuffer: TArray<Single>);var  lTemp, lVertices: TArray<Single>;  i, lVerticesLength, j, n: Integer;  lMinX, lMinY, lMaxX, lMaxY, lVX, lVY: Single;  lSlot: TSpineSlot;  lAttachment: IAttachment;  lRegionAttachment: TRegionAttachment;  lMeshAttachment: TMeshAttachment;begin  lTemp:= AVertexBuffer;  if Length(lTemp) = 0 then    SetLength(lTemp, 8);  lMinX:= Integer.MaxValue;  lMinY:= Integer.MaxValue;  lMaxX:= Integer.MinValue;  lMaxY:= Integer.MinValue;  for i:= 0 to FDrawOrder.Count -1 do  begin    lSlot:= FDrawOrder.Items[i];    lVerticesLength:= 0;    SetLength(lVertices, 0);    lAttachment:= lSlot.Attachment;    lRegionAttachment:= lAttachment as TRegionAttachment;    if Assigned(lRegionAttachment) then    begin      lVerticesLength:= 8;      lVertices:= lTemp;      if Length(lVertices) < 8 then      begin        SetLength(lTemp, 8);        lVertices:= lTemp;        lRegionAttachment.ComputeWorldVertices(lSlot.Bone, lTemp, 0);      end;    end else    begin      lMeshAttachment:= lAttachment as TMeshAttachment;      if Assigned(lMeshAttachment) then      begin        lVerticesLength:= lMeshAttachment.WorldVerticesLength;        lVertices:= lTemp;        if Length(lVertices) < lVerticesLength then        begin          SetLength(lTemp, lVerticesLength);          lVertices:= lTemp;          lMeshAttachment.ComputeWorldVertices(lSlot, 0, lVerticesLength, lTemp, 0);        end;      end;    end;    n:= Length(lVertices);    if n > 0 then    begin      j:= 0;      while j < n do      begin        lVX:= lVertices[j];        lVY:= lVertices[j+1];        lMinX:= Min(lMinX, lVX);        lMinY:= Min(lMinY, lVY);        lMaxX:= Max(lMaxX, lVX);        lMaxY:= Max(lMaxY, lVY);        j:= j + 2;      end;    end;  end;  oX:= lMinX;  oY:= lMinY;  oWidth := lMaxX - lMinX;  oHeight:= lMaxY - lMinY;  AVertexBuffer:= lTemp;end;end.

骨架模块,粗粗检查了一下,没什么问题。

这里说一下,这个库的移植(其实大多是翻译,因为很多代码我根本不理解,但是不影响我翻译就是了)是基于C#的,C#是垃圾回收机制,移到delphi的话,对象的创建、释放就要很注意,不然很容易有内存泄露。

这边检查主要就是检查对象的释放,大多是TList和TObjectList的选择问题。

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台