【Spine】Spine Runtime for Delphi移植筆記(六) - spine.core.skeleton.binary

来源:http://www.cnblogs.com/crymm/archive/2017/08/02/7272679.html
-Advertisement-
Play Games

上一篇翻譯了圖集解析單元,今天把骨架解析也翻譯完了(二進位,.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

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【以前的文章】最後一公裡極速配送 阿裡雲演算法大賽總結 總結一下新的教訓 1.由於都是NP難題,獲得最優解用常規的方法非常困難,對於不是演算法科班出身的人來說,首先應該到網路上尋找一下論文,是否有一些好的經驗。 2.保持平常心,這種比賽獲獎很困難,生活還是要和往常一樣,只是將空餘的時間給做比賽 3.每一 ...
  • 最近在研究pos印表機相關功能, 調用winapi以及跨進程通信等,都涉及到類型之間的轉換。 C/C++ C# HANDLE, LPDWORD, LPVOID, void* IntPtr LPCTSTR, LPCTSTR, LPSTR, char*, const char*, Wchar_t*, L ...
  • 一:Erlang安裝 因為RabbitMQ 是Erlang語言開發的,所以首先要裝上Erlang的環境 1)下載Erlang 下載官網:http://www.erlang.org/download.html。 實踐安裝版本:otp_win64_20.0.exe。(安裝最新版就好)2)安裝Erlang ...
  • 1、開發工具版本 maven:Apache Maven 3.3.9 JDK:1.8.0 Eclipse:Mars.1 Release (4.5.1) (現在eclipse官網上的最新版本) MySQL:mysql-5.6.24-win32(免安裝版,但需要進行相關配置) 將Eclipse直接解壓,由 ...
  • 起因:寫代碼的時候經常會用到獲取當前時間戳和日期,現總結如下 date.toString():Wed Aug 02 11:39:05 CST 2017當前時間戳(毫秒):1501645145298當前日期:2當前年份:2017當前月份:712小時制:當前小時:1124小時制:當前小時:11當前分鐘: ...
  • 骨架模塊,粗粗檢查了一下,沒什麼問題。 這裡說一下,這個庫的移植(其實大多是翻譯,因為很多代碼我根本不理解,但是不影響我翻譯就是了)是基於C#的,C#是垃圾回收機制,移到delphi的話,對象的創建、釋放就要很註意,不然很容易有記憶體泄露。 這邊檢查主要就是檢查對象的釋放,大多是TList和TObje ...
  • n皇後問題:輸入整數n, 要求n個國際象棋的皇後,擺在n*n的棋盤上,互相不能攻擊,輸出全部方案。 代碼如下: ...
  • 在python中,省去了變數聲明的過程,在引用變數時,往往一個簡單的賦值語句就同時完成了,聲明變數類型,變數定義和關聯的過程,那麼python的變數到底是怎樣完成定義的呢? 動態類型 python使用動態類型和他提供的多態性來提供python語言的簡潔靈活的基礎。在python中我們是不會聲明所使用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...