【Spine】Spine Runtime for Delphi移植笔记(七…
2018-06-17 18:56:25来源:未知 阅读 ()
//////////////////////////////////////////////////////////////////////////////// //Generic delphi runtime v3.6 for Spine animation tool // //Runtime port by cjk (hzi1980@163.com) // //////////////////////////////////////////////////////////////////////////////// unit spine.core.skeleton; interface uses 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的选择问题。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:DELPHI 小结
下一篇:Delphi提取PDF文本
- Delphi Format、FormatFloat与FormatDateTime的用法 2020-05-22
- Delphi-基础(for循环) 2019-12-02
- BussinessSkinForm 入门教程 2019-08-16
- Delphi BusinessSkinForm使用说明 2019-08-16
- webform 简单的服务器控件。 2018-06-27
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash