上一篇翻譯了圖集解析單元,今天把骨架解析也翻譯完了(二進位,.skel文件)。1000多行代碼,把我累的。。。 spine.core.bone, spine.core.slot, spine.core.skin, spine.core.attachment, spine.core.constrain ...
//////////////////////////////////////////////////////////////////////////////// //Generic delphi runtime v3.6 for Spine animation tool // //Runtime port by cjk ([email protected]) // //////////////////////////////////////////////////////////////////////////////// unit spine.core.skeleton.binary; interface uses System.Classes, System.SysUtils, System.Generics.Collections, System.Math, spine.types, spine.classes, spine.data, spine.core.atlas, spine.core.bone, spine.core.slot, spine.core.skin, spine.core.attachment, spine.core.constraint, spine.core.skeleton, spine.core.animation.timeline, spine.core.skeleton.json, spine.core.event, spine.core.animation; type SByte = ShortInt; TSpineSkeletonBinary = class public const BONE_ROTATE = 0; BONE_TRANSLATE = 1; BONE_SCALE = 2; BONE_SHEAR = 3; SLOT_ATTACHMENT = 0; SLOT_COLOR = 1; SLOT_TWO_COLOR = 2; PATH_POSITION = 0; PATH_SPACING = 1; PATH_MIX = 2; CURVE_LINEAR = 0; CURVE_STEPPED = 1; CURVE_BEZIER = 2; private type TVertices = record Bones: TArray<Integer>; Vertices: TArray<Single>; end; private FLinkedMeshes: TObjectList<TSkeletonJson.TLinkedMesh>; FAttachmentLoader: TAttachmentLoader; FBuffer: array [0..31] of Byte; function ReadByte(const AStream: TStream): Integer; function ReadString(const AStream: TStream): string; function ReadInt(const AStream: TStream): Integer; function ReadFloat(const AStream: TStream): Single; function ReadBoolean(const AStream: TStream): Boolean; function ReadSByte(const AStream: TStream): SByte; function ReadVarInt(const AStream: TStream; const AOptimizePositive: Boolean): Integer; function ReadShortArray(const AStream: TStream): TArray<Integer>; function ReadFloatArray(const AStream: TStream; const ACount: Integer; const AScale: Single): TArray<Single>; function ReadVertices(const AStream: TStream; const AVertexCount: Integer): TVertices; function ReadAttachment(const AStream: TStream; const ASkeletonData: TSkeletonData; const ASkin: TSpineSkin; const ASlotIndex: Integer; const AAttachmentName: string; const ANonessential: Boolean): IAttachment; function ReadSkin (const AStream: TStream; const ASkeletonData: TSkeletonData; const ASkinName: string; const ANonessential: Boolean): TSpineSkin; procedure ReadCurve(const AStream: TStream; const AFrameIndex: Integer; const ATimeline: TCurveTimeline); procedure ReadAnimation(const AName: string; const AStream: TStream; const ASkeletonData: TSkeletonData); class procedure ReadFully(const AStream: TStream; var ABuffer: TArray<Byte>; const AOffset: Integer; var ALength: Integer); static; public Scale: Single; constructor Create(const AAtlasArray: TArray<TSpineAtlas>); overload; constructor Create(const AAttachmentLoader: TAttachmentLoader); overload; destructor Destroy; override; function ReadSkeletonData(const ASkelFile: string): TSkeletonData; overload; function ReadSkeletonData(const ASkelStream: TStream): TSkeletonData; overload; end; implementation { TSpineSkeletonBinary } constructor TSpineSkeletonBinary.Create(const AAtlasArray: TArray<TSpineAtlas>); begin FAttachmentLoader:= TAtlasAttachmentLoader.Create(AAtlasArray); Create(FAttachmentLoader); end; constructor TSpineSkeletonBinary.Create( const AAttachmentLoader: TAttachmentLoader); begin inherited Create; if not Assigned(AAttachmentLoader) then raise Exception.Create('attachmentLoader cannot be null.'); FLinkedMeshes:= TObjectList<TSkeletonJson.TLinkedMesh>.Create; FAttachmentLoader:= AAttachmentLoader; end; destructor TSpineSkeletonBinary.Destroy; begin FLinkedMeshes.Free; if Assigned(FAttachmentLoader) then FreeAndNil(FAttachmentLoader); inherited; end; function TSpineSkeletonBinary.ReadSkeletonData( const ASkelFile: string): TSkeletonData; var lStream: TFileStream; begin lStream:= TFileStream.Create(ASkelFile, fmOpenRead); try result:= Self.ReadSkeletonData(lStream); finally lStream.Free; end; end; function TSpineSkeletonBinary.ReadSkeletonData( const ASkelStream: TStream): TSkeletonData; var lScale: Single; lNonessential: Boolean; i, j, n, nn: Integer; lName: string; lBoneDataParent, lBoneData: TBoneData; lColor, lDarkColor: Integer; lSlotData: TSlotData; lIkConstraintData: TIkConstraintData; lTransformConstraintData: TTransformConstraintData; lPathConstraintData: TPathConstraintData; lDefaultSkin, lSkin: TSpineSkin; lLinkedMesh: TSkeletonJson.TLinkedMesh; lParentAttachment: IAttachment; lEventData: TEventData; begin if not Assigned(ASkelStream) then raise Exception.Create('skelstream cannot be null.'); result:= TSkeletonData.Create; result.Name:= ChangeFileExt(ExtractFileName(TFileStream(ASkelStream).FileName),''); result.Hash:= Self.ReadString(ASkelStream); result.Version:= Self.ReadString(ASkelStream); result.Width:= Self.ReadFloat(ASkelStream); result.Height:= Self.ReadFloat(ASkelStream); lNonessential:= Self.ReadBoolean(ASkelStream); if lNonessential then begin result.FPS:= Self.ReadFloat(ASkelStream); result.ImagesPath:= Self.ReadString(ASkelStream); end; // Bones. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do begin lName:= Self.ReadString(ASkelStream); if i = 0 then lBoneDataParent:= nil else lBoneDataParent:= result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]; lBoneData:= TBoneData.Create(i, lName, lBoneDataParent); lBoneData.Rotation:= Self.ReadFloat(ASkelStream); lBoneData.X:= Self.ReadFloat(ASkelStream) * Self.Scale; lBoneData.Y:= Self.ReadFloat(ASkelStream) * Self.Scale; lBoneData.ScaleX:= Self.ReadFloat(ASkelStream); lBoneData.ScaleY:= Self.ReadFloat(ASkelStream); lBoneData.ShearX:= Self.ReadFloat(ASkelStream); lBoneData.ShearY:= Self.ReadFloat(ASkelStream); lBoneData.Length:= Self.ReadFloat(ASkelStream) * Self.Scale; //lBoneData.TransformMode:= TTransformModes[Self.ReadVarInt(ASkelStream, True)]; if lNonessential then Self.ReadInt(ASkelStream); // Skip bone color. result.BoneDatas.Add(lBoneData); end; // Slots. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do begin lName:= Self.ReadString(ASkelStream); lBoneData:= result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]; lSlotData:= TSlotData.Create(i, lName, lBoneData); lColor:= Self.ReadInt(ASkelStream); lSlotData.R:= ((lColor and $ff) shr 24) / 255; lSlotData.G:= ((lColor and $00ff) shr 16) / 255; lSlotData.B:= ((lColor and $0000ff) shr 8) / 255; lSlotData.A:= (lColor and $000000ff) / 255; lDarkColor:= Self.ReadInt(ASkelStream); // 0x00rrggbb if lDarkColor <> -1 then begin lSlotData.HasSecondColor:= True; lSlotData.R2:= ((lDarkColor and $00ff) shr 16) / 255; lSlotData.G2:= ((lDarkColor and $0000ff) shr 8) / 255; lSlotData.B2:= (lDarkColor and $000000ff) / 255; end; lSlotData.AttachmentName:= Self.ReadString(ASkelStream); lSlotData.BlendMode:= TBlendMode(Self.ReadVarInt(ASkelStream, True)); result.SlotDatas.Add(lSlotData); end; // IK constraints. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do begin lIkConstraintData:= TIkConstraintData.Create(Self.ReadString(ASkelStream)); lIkConstraintData.Order:= Self.ReadVarInt(ASkelStream, True); nn:= Self.ReadVarInt(ASkelStream, True); for j:= 0 to nn -1 do lIkConstraintData.BoneDatas.Add(result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]); lIkConstraintData.Target:= result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]; lIkConstraintData.Mix:= Self.ReadFloat(ASkelStream); lIkConstraintData.BendDirection:= Self.ReadSByte(ASkelStream); result.IkConstraintDatas.Add(lIkConstraintData); end; // Transform constraints. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do begin lTransformConstraintData:= TTransformConstraintData.Create(Self.ReadString(ASkelStream)); lTransformConstraintData.Order:= Self.ReadVarInt(ASkelStream, True); nn:= Self.ReadVarInt(ASkelStream, True); for j:= 0 to nn -1 do lTransformConstraintData.BoneDatas.Add(result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]); lTransformConstraintData.Target:= result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]; lTransformConstraintData.Local:= Self.ReadBoolean(ASkelStream); lTransformConstraintData.Relative:= Self.ReadBoolean(ASkelStream); lTransformConstraintData.OffsetRotation:= Self.ReadFloat(ASkelStream); lTransformConstraintData.OffsetX:= Self.ReadFloat(ASkelStream) * Self.Scale; lTransformConstraintData.OffsetY:= Self.ReadFloat(ASkelStream) * Self.Scale; lTransformConstraintData.OffsetScaleX:= Self.ReadFloat(ASkelStream); lTransformConstraintData.OffsetScaleY:= Self.ReadFloat(ASkelStream); lTransformConstraintData.OffsetShearY:= Self.ReadFloat(ASkelStream); lTransformConstraintData.RotateMix:= Self.ReadFloat(ASkelStream); lTransformConstraintData.TranslateMix:= Self.ReadFloat(ASkelStream); lTransformConstraintData.ScaleMix:= Self.ReadFloat(ASkelStream); lTransformConstraintData.ShearMix:= Self.ReadFloat(ASkelStream); result.TransformConstraintDatas.Add(lTransformConstraintData); end; // Path constraints n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do begin lPathConstraintData:= TPathConstraintData.Create(Self.ReadString(ASkelStream)); lPathConstraintData.Order:= Self.ReadVarInt(ASkelStream, True); nn:= Self.ReadVarInt(ASkelStream, True); for j:= 0 to nn -1 do lPathConstraintData.BoneDatas.Add(result.BoneDatas.Items[Self.ReadVarInt(ASkelStream, True)]); lPathConstraintData.Target:= result.SlotDatas.Items[Self.ReadVarint(ASkelStream, true)]; lPathConstraintData.PositionMode:= TPositionMode(Self.ReadVarint(ASkelStream, true)); lPathConstraintData.SpacingMode:= TSpacingMode(Self.ReadVarint(ASkelStream, true)); lPathConstraintData.RotateMode:= TRotateMode(Self.ReadVarint(ASkelStream, true)); lPathConstraintData.OffsetRotation:= Self.ReadFloat(ASkelStream); lPathConstraintData.Position:= Self.ReadFloat(ASkelStream); if lPathConstraintData.PositionMode = TPositionMode.pmFixed then lPathConstraintData.Position:= lPathConstraintData.Position * Self.Scale; lPathConstraintData.Spacing:= Self.ReadFloat(ASkelStream); if (lPathConstraintData.SpacingMode = TSpacingMode.smLength) or (lPathConstraintData.SpacingMode = TSpacingMode.smFixed) then lPathConstraintData.Spacing:= lPathConstraintData.Spacing * Self.Scale; lPathConstraintData.RotateMix:= Self.ReadFloat(ASkelStream); lPathConstraintData.TranslateMix:= Self.ReadFloat(ASkelStream); result.PathConstraintDatas.Add(lPathConstraintData); end; // Default skin. lDefaultSkin:= Self.ReadSkin(ASkelStream, result, 'default', lNonessential); if Assigned(lDefaultSkin) then begin result.DefaultSkin:= lDefaultSkin; result.Skins.Add(lDefaultSkin); end; // Skins. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do result.Skins.Add(Self.ReadSkin(ASkelStream, result, Self.ReadString(ASkelStream), lNonessential)); // Linked meshes. n:= FLinkedMeshes.Count; for i:= 0 to n -1 do begin lLinkedMesh:= FLinkedMeshes[i]; if not Assigned(lLinkedMesh) then lSkin:= lDefaultSkin else lSkin:= result.FindSkin(lLinkedMesh.Skin); if not Assigned(lSkin) then raise Exception.CreateFmt('Skin not found: %s',[lLinkedMesh.Skin]); lParentAttachment:= lSkin.GetAttachment(lLinkedMesh.SlotIndex, lLinkedMesh.Parent); if not Assigned(lParentAttachment) then raise Exception.CreateFmt('Parent mesh not found: %s',[lLinkedMesh.Parent]); lLinkedMesh.Mesh.ParentMesh:= TMeshAttachment(lParentAttachment); lLinkedMesh.Mesh.UpdateUVs; end; FLinkedMeshes.Clear; // Events. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do begin lEventData:= TEventData.Create(Self.ReadString(ASkelStream)); lEventData.IntValue:= Self.ReadVarInt(ASkelStream, False); lEventData.FloatValue:= Self.ReadFloat(ASkelStream); lEventData.StringValue:= Self.ReadString(ASkelStream); result.EventDatas.Add(lEventData); end; // Animations. n:= Self.ReadVarInt(ASkelStream, True); for i:= 0 to n -1 do Self.ReadAnimation(Self.ReadString(ASkelStream), ASkelStream, result); result.BoneDatas.TrimExcess; result.SlotDatas.TrimExcess; result.Skins.TrimExcess; result.EventDatas.TrimExcess; result.Animations.TrimExcess; result.IkConstraintDatas.TrimExcess; result.TransformConstraintDatas.TrimExcess; result.PathConstraintDatas.TrimExcess; end; function TSpineSkeletonBinary.ReadByte(const AStream: TStream): Integer; begin if AStream.Position + 1 > AStream.Size then exit(-1); AStream.Read(result, 1); end; function TSpineSkeletonBinary.ReadSkin(const AStream: TStream; const ASkeletonData: TSkeletonData; const ASkinName: string; const ANonessential: Boolean): TSpineSkin; var lSlotCount, i, lSlotIndex, j, n: Integer; lName: string; lAttachment: IAttachment; begin lSlotCount:= Self.ReadVarInt(AStream, True); if lSlotCount = 0 then exit(nil); result:= TSpineSkin.Create; for i:= 0 to lSlotCount -1 do begin lSlotIndex:= Self.ReadVarInt(AStream, True); n:= Self.ReadVarInt(AStream, True); for j:= 0 to n -1 do begin lName:= Self.ReadString(AStream); lAttachment:= Self.ReadAttachment(AStream, ASkeletonData, result, lSlotIndex, lName, ANonessential); if Assigned(lAttachment) then result.AddAttachment(lSlotIndex, lName, lAttachment); end; end; end; function TSpineSkeletonBinary.ReadAttachment(const AStream: TStream; const ASkeletonData: TSkeletonData; const ASkin: TSpineSkin; const ASlotIndex: Integer; const AAttachmentName: string; const ANonessential: Boolean): IAttachment; var lName, lPath: string; lAttachmentType: TAttachmentType; lRotation, lX, lY, lScaleX, lScaleY, lWidth, lHeight: Single; lColor: Integer; lRegionAttachment: TRegionAttachment; lVertexCount: Integer; lVertices: TVertices; lBoxAttachment: TBoundingBoxAttachment; lUVs: TArray<Single>; lTriangles, lEdges: TArray<Integer>; lHullLength: Integer; lMeshAttachment: TMeshAttachment; lSkinName, lParentMeshName: string; lInheritDeform: Boolean; lClosed, lConstantSpeed: Boolean; lLengths: TArray<Single>; i: Integer; lPathAttachment: TPathAttachment; lPointAttachment: TPointAttachment; lEndSlotIndex: Integer; lClippingAttachment: TClippingAttachment; begin lName:= Self.ReadString(AStream); if lName.Trim.IsEmpty then lName:= AAttachmentName; lAttachmentType:= TAttachmentType(Self.ReadByte(AStream)); case lAttachmentType of TAttachmentType.atRegion: begin lPath:= Self.ReadString(AStream); lRotation:= Self.ReadFloat(AStream); lX:= Self.ReadFloat(AStream); lY:= Self.ReadFloat(AStream); lScaleX:= Self.ReadFloat(AStream); lScaleY:= Self.ReadFloat(AStream); lWidth:= Self.ReadFloat(AStream); lHeight:= Self.ReadFloat(AStream); lColor:= Self.ReadInt(AStream); if lPath.Trim.IsEmpty then lPath:= lName; // lRegionAttachment:= FAttachmentLoader.NewRegionAttachment(ASkin, lName, lPath); if not Assigned(lRegionAttachment) then exit(nil); lRegionAttachment.Path:= lPath; lRegionAttachment.X:= lX * Self.Scale; lRegionAttachment.Y:= lY * Self.Scale; lRegionAttachment.ScaleX:= lScaleX; lRegionAttachment.ScaleY:= lScaleY; lRegionAttachment.Rotation:= lRotation; lRegionAttachment.Width:= lWidth * Self.Scale; lRegionAttachment.Height:= lHeight * Self.Scale; lRegionAttachment.R:= ((lColor and $ff) shr 24) / 255; lRegionAttachment.G:= ((lColor and $00ff) shr 16) / 255; lRegionAttachment.B:= ((lColor and $0000ff) shr 8) / 255; lRegionAttachment.A:= ((lColor and $000000ff)) / 255; lRegionAttachment.UpdateOffset(); exit(lRegionAttachment); end; TAttachmentType.atBoundingbox: begin lVertexCount:= Self.ReadVarint(AStream, True); lVertices:= Self.ReadVertices(AStream, lVertexCount); if ANonessential then Self.ReadInt(AStream); // lBoxAttachment:= FAttachmentLoader.NewBoundingBoxAttachment(ASkin, lName); if not Assigned(lBoxAttachment) then exit(nil); lBoxAttachment.WorldVerticesLength:= lVertexCount shl 1; SetLength(lBoxAttachment.Vertices, Length(lVertices.Vertices)); SetLength(lBoxAttachment.Bones, Length(lVertices.Bones)); TArray.Copy<Single>(lVertices.Vertices, lBoxAttachment.Vertices, 0, 0, Length(lVertices.Vertices)); TArray.Copy<Integer>(lVertices.Bones, lBoxAttachment.Bones, 0, 0, Length(lVertices.Bones)); exit(lBoxAttachment); end; TAttachmentType.atMesh: begin lPath:= Self.ReadString(AStream); lColor:= Self.ReadInt(AStream); lVertexCount:= Self.ReadVarInt(AStream, True); lUVs:= Self.ReadFloatArray(AStream, lVertexCount shl 1, 1); lTriangles:= Self.ReadShortArray(AStream); lVertices:= Self.ReadVertices(AStream, lVertexCount); lHullLength:= Self.ReadVarInt(AStream, True); lWidth:= 0; lHeight:= 0; if ANonessential then begin lEdges:= Self.ReadShortArray(AStream); lWidth:= Self.ReadFloat(AStream); lHeight:= Self.ReadFloat(AStream); end; if lPath.Trim.IsEmpty then lPath:= lName; lMeshAttachment:= FAttachmentLoader.NewMeshAttachment(ASkin, lName, lPath); if not Assigned(lMeshAttachment) then exit(nil); lMeshAttachment.Path:= lPath; lMeshAttachment.R:= ((lColor and $ff) shr 24) / 255; lMeshAttachment.G:= ((lColor and $00ff) shr 16) / 255; lMeshAttachment.B:= ((lColor and $0000ff) shr 8) / 255; lMeshAttachment.A:= ((lColor and $000000ff)) / 255; SetLength(lMeshAttachment.Vertices, Length(lVertices.Vertices)); SetLength(lMeshAttachment.Bones, Length(lVertices.Bones)); TArray.Copy<Single>(lVertices.Vertices, lMeshAttachment.Vertices, 0, 0, Length(lVertices.Vertices)); TArray.Copy<Integer>(lVertices.Bones, lMeshAttachment.Bones, 0, 0, Length(lVertices.Bones)); lMeshAttachment.WorldVerticesLength:= lVertexCount shl 1; SetLength(lMeshAttachment.Triangles, Length(lTriangles)); SetLength(lMeshAttachment.RegionUVs, Length(lUVs)); TArray.Copy<Integer>(lTriangles, lMeshAttachment.Triangles, 0, 0, Length(lTriangles)); TArray.Copy<Single>(lUVs, lMeshAttachment.RegionUVs, 0, 0, Length(lUVs)); lMeshAttachment.UpdateUVs(); lMeshAttachment.HullLength:= lHullLength shl 1; if ANonessential then begin TArray.Copy<Integer>(lEdges, lMeshAttachment.Edges, 0, 0, Length(lEdges)); lMeshAttachment.Width:= lWidth * Self.Scale; lMeshAttachment.Height:= lHeight * Self.Scale; end; exit(lMeshAttachment); end; TAttachmentType.atLinkedmesh: begin lPath:= Self.ReadString(AStream); lColor:= Self.ReadInt(AStream); lSkinName:= Self.ReadString(AStream); lParentMeshName:= Self.ReadString(AStream); lInheritDeform:= Self.ReadBoolean(AStream); lWidth:= 0; lHeight:= 0; if ANonessential then begin lWidth:= Self.ReadFloat(AStream); lHeight:= Self.ReadFloat(AStream); end; if lPath.Trim.IsEmpty then lPath:= lName; lMeshAttachment:= FAttachmentLoader.NewMeshAttachment(ASkin, lName, lPath); if not Assigned(lMeshAttachment) then exit(nil); lMeshAttachment.Path:= lPath; lMeshAttachment.R:= ((lColor and $ff) shr 24) / 255; lMeshAttachment.G:= ((lColor and $00ff) shr 16) / 255; lMeshAttachment.B:= ((lColor and $0000ff) shr 8) / 255; lMeshAttachment.A:= ((lColor and $000000ff)) / 255; lMeshAttachment.InheritDeform:= lInheritDeform; if ANonessential then begin lMeshAttachment.Width:= lWidth * Self.Scale; lMeshAttachment.Height:= lHeight * Self.Scale; end; FLinkedMeshes.Add(TSkeletonJson.TLinkedMesh.Create(lMeshAttachment, lSkinName, ASlotIndex, lParentMeshName)); exit(lMeshAttachment); end; TAttachmentType.atPath: begin lClosed:= Self.ReadBoolean(AStream); lConstantSpeed:= Self.ReadBoolean(AStream); lVertexCount:= Self.ReadVarint(AStream, True); lVertices:= Self.ReadVertices(AStream, lVertexCount); SetLength(lLengths, System.Math.Floor(lVertexCount / 3)); for i:= 0 to Length(lLengths) -1 do lLengths[i]:= Self.ReadFloat(AStream) * Self.Scale; if ANonessential then Self.ReadInt(AStream); // lPathAttachment:= FAttachmentLoader.NewPathAttachment(ASkin, lName); if not Assigned(lPathAttachment) then exit(nil); lPathAttachment.Closed:= lClosed; lPathAttachment.ConstantSpeed:= lConstantSpeed; lPathAttachment.WorldVerticesLength:= lVertexCount shl 1; SetLength(lPathAttachment.Vertices, Length(lVertices.Vertices)); SetLength(lPathAttachment.Bones, Length(lVertices.Bones)); SetLength(lPathAttachment.Lengths, Length(lLengths)); TArray.Copy<Single>(lVertices.Vertices, lPathAttachment.Vertices, 0, 0, Length(lVertices.Vertices)); TArray.Copy<Integer>(lVertices.Bones, lPathAttachment.Bones, 0, 0, Length(lVertices.Bones)); TArray.Copy<Single>(lLengths, lPathAttachment.Lengths, 0, 0, Length(lLengths)); exit(lPathAttachment); end; TAttachmentType.atPoint: begin lRotation:= Self.ReadFloat(AStream); lX:= Self.ReadFloat(AStream); lY:= Self.ReadFloat(AStream); if ANonessential then Self.ReadInt(AStream); // lPointAttachment:= FAttachmentLoader.NewPointAttachment(ASkin, lName); if not Assigned(lPointAttachment) then exit(nil); lPointAttachment.X:= lX * Self.Scale; lPointAttachment.Y:= lY * Self.Scale; lPointAttachment.Rotation:= lRotation; exit(lPointAttachment); end; TAttachmentType.atClipping: begin lEndSlotIndex:= Self.ReadVarint(AStream, True); lVertexCount:= Self.ReadVarint(AStream, True); lVertices:= Self.ReadVertices(AStream, lVertexCount); if ANonessential then Self.ReadInt(AStream); // lClippingAttachment:= FAttachmentLoader.NewClippingAttachment(ASkin, lName); if not Assigned(lClippingAttachment) then exit(nil); lClippingAttachment.EndSlot:= ASkeletonData.SlotDatas.Items[lEndSlotIndex]; lClippingAttachment.worldVerticesLength:= lVertexCount shl 1; SetLength(lPathAttachment.Vertices, Length(lVertices.Vertices)); SetLength(lPathAttachment.Bones, Length(lVertices.Bones)); TArray.Copy<Single>(lVertices.Vertices, lClippingAttachment.Vertices, 0, 0, Length(lVertices.Vertices)); TArray.Copy<Integer>(lVertices.Bones, lClippingAttachment.Bones, 0, 0, Length(lVertices.Bones)); exit(lClippingAttachment); end; end; result:= nil; end; function TSpineSkeletonBinary.ReadVertices(const AStream: TStream; const AVertexCount: Integer): TVertices; var lVerticesLength, i, lBoneCount, j, idx1, idx2: Integer; lWeights: TArray<Single>; begin lVerticesLength:= AVertexCount shl 1; if not Self.ReadBoolean(AStream) then begin result.Vertices:= Self.ReadFloatArray(AStream, lVerticesLength, Self.Scale); exit; end; SetLength(result.Vertices, lVerticesLength * 3 * 3); SetLength(result.Bones, lVerticesLength * 3); idx1:= 0; idx2:= 0; for i:= 0 to AVertexCount -1 do begin lBoneCount:= Self.ReadVarInt(AStream, True); result.Bones[idx1]:= Self.ReadVarInt(AStream, True); Inc(idx1); for j:= 0 to lBoneCount -1 do begin result.Bones[idx1]:= Self.ReadVarInt(AStream, True); Inc(idx1); // result.Vertices[idx2]:= Self.ReadFloat(AStream) * Self.Scale; Inc(idx2); result.Vertices[idx2]:= Self.ReadFloat(AStream) * Self.Scale; Inc(idx2); result.Vertices[idx2]:= Self.ReadFloat(AStream); Inc(idx2); end; end; end; function TSpineSkeletonBinary.ReadFloatArray(const AStream: TStream; const ACount: Integer; const AScale: Single): TArray<Single>; var i: Integer; begin SetLength(result, ACount); if AScale = 1 then begin for i:= 0 to ACount -1 do result[i]:= Self.ReadFloat(AStream); end else begin for i:= 0 to ACount -1 do result[i]:= Self.ReadFloat(AStream) * AScale; end; end; function TSpineSkeletonBinary.ReadShortArray( const AStream: TStream): TArray<Integer>; var lCount, i: Integer; begin lCount:= Self.ReadVarInt(AStream, True); SetLength(result, lCount); for i:= 0 to lCount -1 do result[i]:= (Self.ReadByte(AStream) shl 8) or Self.ReadByte(AStream); end; procedure TSpineSkeletonBinary.ReadAnimation(const AName: string; const AStream: TStream; const ASkeletonData: TSkeletonData); var lAnimation: TSpineAnimation; lDuration: Single; i, n, lIndex, j, nn, lTimelineType, lFrameCount, k, nnn: Integer; lAttachmentTimeline: TAttachmentTimeline; lFrameIndex: Integer; lColorTimeline: TColorTimeline; lTime, lR, lG, lB, lA, lR2, lG2, lB2: Single; lColor, lColor2: Integer; lTwoColorTimeline: TTwoColorTimeline; lRotateTimeline: TRotateTimeline; lTranslateTimeline: TTranslateTimeline; lTimelineScale: Single; lIkConstraintTimeline: TIkConstraintTimeline; lTransformConstraintTimeline: TTransformConstraintTimeline; lPathConstraintData: TPathConstr