骨架模塊,粗粗檢查了一下,沒什麼問題。 這裡說一下,這個庫的移植(其實大多是翻譯,因為很多代碼我根本不理解,但是不影響我翻譯就是了)是基於C#的,C#是垃圾回收機制,移到delphi的話,對象的創建、釋放就要很註意,不然很容易有記憶體泄露。 這邊檢查主要就是檢查對象的釋放,大多是TList和TObje ...
//////////////////////////////////////////////////////////////////////////////// //Generic delphi runtime v3.6 for Spine animation tool // //Runtime port by cjk ([email protected]) // //////////////////////////////////////////////////////////////////////////////// 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的話