【Spine】Spine Runtime for Delphi移植笔记(五) - spine.core.atlas

////////////////////////////////////////////////////////////////////////////////
//Generic delphi runtime v3.6 for Spine animation tool                        //
//Runtime port by cjk (hzi1980@163.com)                                       //
////////////////////////////////////////////////////////////////////////////////

unit spine.core.atlas;

interface

uses
  System.Classes, System.SysUtils, System.Generics.Collections, System.TypInfo,
  System.Math, spine.types, spine.classes;

type
  TSpineAtlas = class(IAtlas)
  private type
    TupleStringArray = array [0..3] of string;
  private
    FPages: TObjectList<TAtlasPage>;
    FRegions: TObjectDictionary<string, TAtlasRegion>;
    FTextureLoader: ITextureLoader;
    procedure Load(const AStream: TStream);
  public
    constructor Create(const AStream: TStream; const ATextureLoader: ITextureLoader);
    destructor Destroy; override;
  end;

implementation

{ TSpineAtlas }

constructor TSpineAtlas.Create(const AStream: TStream;
  const ATextureLoader: ITextureLoader);
begin
  inherited Create;
  FPages:= TObjectList<TAtlasPage>.Create;
  FRegions:= TObjectDictionary<string,TAtlasRegion>.Create([doOwnsValues]);
  FTextureLoader:= ATextureLoader;
  Load(AStream);
end;

destructor TSpineAtlas.Destroy;
begin
  FPages.Free;
  FRegions.Free;
  inherited;
end;

procedure TSpineAtlas.Load(const AStream: TStream);
var
  EndOf: Boolean;

  function ReadLn(): string;
  var
    b, b1: Byte;
  begin
    Result:= '';
    if AStream.Position = AStream.Size then
    begin
      EndOf:= True;
      Exit;
    end;
    //
    while AStream.Position < AStream.Size do
    begin
      {$Hints off}
      AStream.Read(b, 1);
      {$Hints on}
      if (b <> $D) and (b <> $A) then
        Result:= Result + AnsiChar(Chr(b))
      else
        Break;
    end;
    //
    if AStream.Position < AStream.Size then
      {$Hints off}
      AStream.Read(b1, 1)
      {$Hints on}
    else Exit;
    //
    while ((b1 = $D) or (b1 = $A)) and (b1 <> b) and (AStream.Position < AStream.Size) do
      AStream.Read(b1, 1);
    //
    if AStream.Position < AStream.Size then
      AStream.Position:= AStream.Position - 1;
  end;

  function ReadTuple(out ATuple: TupleStringArray): Integer;
  var
    ln: string;
    colon, i, lastMatch, comma: Integer;
  begin
    ln:= Trim(ReadLn);
    colon:= ln.IndexOf(':');
    if colon = -1 then raise Exception.CreateFmt('Invalid line:%s',[ln]);
    lastMatch:= colon + 1;
    for i:= 0 to 2 do
    begin
      comma:= ln.IndexOf(',', lastMatch);
      if comma = -1 then break;
      ATuple[i]:= ln.Substring(lastMatch, comma - lastMatch).Trim;
      lastMatch:= comma + 1;
    end;
    ATuple[i]:= ln.Substring(lastMatch).Trim;
    result:= i + 1;
  end;

  function ReadValue: string;
  var
    ln: string;
    colon, i, lastMatch, comma: Integer;
  begin
    ln:= Trim(ReadLn);
    colon:= ln.IndexOf(':');
    if colon = -1 then raise Exception.CreateFmt('Invalid line:%s',[ln]);
        result:= ln.Substring(colon + 1).Trim;
  end;
  
var
  Page: TAtlasPage;
  Region: TAtlasRegion;
  Line, Direction: string;
  Tuple: TupleStringArray;
  i: Integer;  
begin
  EndOf:= False;
  Page:= nil;
  Region:= nil;
  while not EndOf do
  begin
    Line:= Trim(ReadLn);
    if Length(Line) > 0 then
    begin
      if Page = nil then
      begin
        Page:= TAtlasPage.Create;
        Page.Name:= Line;
        //
        if ReadTuple(Tuple) = 2 then
        begin
          Page.Width := Tuple[0].ToInteger;
                    Page.Height:= Tuple[1].ToInteger;
                    ReadTuple(Tuple);
                end;
        Page.Format:= TPageFormat(GetEnumValue(TypeInfo(TPageFormat), 'pf'+Tuple[0]));
        //
        ReadTuple(Tuple);
        Page.MinFilter:= TPageTextureFilter(GetEnumValue(TypeInfo(TPageTextureFilter), 'ptf'+Tuple[0])); 
        Page.MagFilter:= TPageTextureFilter(GetEnumValue(TypeInfo(TPageTextureFilter), 'ptf'+Tuple[1]));
        //
        Direction:= ReadValue;
        Page.WrapU:= TPageTextureWrap.ptwClampToEdge;
        Page.WrapV:= TPageTextureWrap.ptwClampToEdge;
        if Direction = 'x' then
          Page.WrapU:= TPageTextureWrap.ptwRepeat
        else if Direction = 'y' then
          Page.WrapV:= TPageTextureWrap.ptwRepeat
        else if Direction = 'xy' then
        begin
          Page.WrapU:= TPageTextureWrap.ptwRepeat;
          Page.WrapV:= TPageTextureWrap.ptwRepeat;
        end;
        //
        FTextureLoader.LoadTexture(Page, Line);
        FPages.Add(Page);
      end else
      begin
        Region:= TAtlasRegion.Create;
        Region.Name:= Line; 
        Region.Rotate:= ReadValue.ToBoolean;
              ReadTuple(Tuple);
        Region.X:= Tuple[0].ToInteger;
        Region.Y:= Tuple[1].ToInteger;
                ReadTuple(Tuple);
        Region.Width := Abs(Tuple[0].ToInteger);
        Region.Height:= Abs(Tuple[1].ToInteger);
        Region.U:= Region.X / Page.Width;
        Region.V:= Region.Y / Page.Height;
        if Region.Rotate then
        begin
          Region.U2:= (Region.X + Region.Height) / Page.Width;
          Region.V2:= (Region.Y + Region.Width) / Page.Height; 
        end else
        begin
          Region.U2:= (Region.X + Region.Width) / Page.Width;
          Region.V2:= (Region.Y + Region.Height) / Page.Height; 
        end;
        //
        if ReadTuple(Tuple) = 4 then
        begin        
          SetLength(Region.Splits, 4);
          Region.Splits[0]:= Tuple[0].ToInteger;
          Region.Splits[1]:= Tuple[1].ToInteger;
          Region.Splits[2]:= Tuple[2].ToInteger;
          Region.Splits[3]:= Tuple[3].ToInteger;
          if ReadTuple(Tuple) = 4 then
          begin
            SetLength(Region.Pads, 4);
            Region.Pads[0]:= Tuple[0].ToInteger;
            Region.Pads[1]:= Tuple[1].ToInteger;
            Region.Pads[2]:= Tuple[2].ToInteger;
            Region.Pads[3]:= Tuple[3].ToInteger;            
            ReadTuple(Tuple);
          end;          
        end;
        Region.OriginalWidth := Tuple[0].ToInteger;
        Region.OriginalHeight:= Tuple[1].ToInteger;
        //
        ReadTuple(Tuple);
        Region.OffsetX:= Tuple[0].ToInteger;
        Region.OffsetY:= Tuple[1].ToInteger;
        Region.Index:= ReadValue.ToInteger;
        FRegions.Add(Line,Region);
      end;
    end else
    begin
      Page:= nil;
      Region:= nil;
    end;
  end;
end;

end.

图集(.atlas)的解析单元,这个单元比较独立,参考了一些别的代码。

这里说说atlas这个东西。

像下面这张图,就是一个图集,里面有我们要表现的角色动画的头、手、身体等部位。

.atlas这个文件可以看做是图片中这些部件的纹理数据,上面的解析代码就是对这个文件进行解析,还原成一些对象,便于后续渲染中取值。

【goblins.png】

 

【goblins.atlas】

goblins.png
format: RGBA8888
filter: Linear,Linear
repeat: none
spear
  rotate: false
  xy: 2, 142
  size: 22, 368
  orig: 22, 368
  offset: 0, 0
  index: -1
goblingirl/head
  rotate: false
  xy: 26, 429
  size: 103, 81
  orig: 103, 81
  offset: 0, 0
  index: -1
goblin/head
  rotate: false
  xy: 26, 361
  size: 103, 66
  orig: 103, 66
  offset: 0, 0
  index: -1
goblin/torso
  rotate: false
  xy: 131, 414
  size: 68, 96
  orig: 68, 96
  offset: 0, 0
  index: -1
goblingirl/torso
  rotate: false
  xy: 26, 263
  size: 68, 96
  orig: 68, 96
  offset: 0, 0
  index: -1
dagger
  rotate: false
  xy: 26, 153
  size: 26, 108
  orig: 26, 108
  offset: 0, 0
  index: -1
goblin/right-lower-leg
  rotate: false
  xy: 201, 434
  size: 36, 76
  orig: 36, 76
  offset: 0, 0
  index: -1
goblingirl/right-lower-leg
  rotate: false
  xy: 54, 185
  size: 36, 76
  orig: 36, 76
  offset: 0, 0
  index: -1
goblin/left-upper-leg
  rotate: false
  xy: 96, 286
  size: 33, 73
  orig: 33, 73
  offset: 0, 0
  index: -1
goblin/pelvis
  rotate: false
  xy: 131, 369
  size: 62, 43
  orig: 62, 43
  offset: 0, 0
  index: -1
goblingirl/pelvis
  rotate: false
  xy: 131, 324
  size: 62, 43
  orig: 62, 43
  offset: 0, 0
  index: -1
goblin/right-foot
  rotate: false
  xy: 131, 289
  size: 63, 33
  orig: 63, 33
  offset: 0, 0
  index: -1
goblin/left-lower-leg
  rotate: false
  xy: 2, 70
  size: 33, 70
  orig: 33, 70
  offset: 0, 0
  index: -1
goblin/right-upper-leg
  rotate: false
  xy: 2, 5
  size: 34, 63
  orig: 34, 63
  offset: 0, 0
  index: -1
goblingirl/left-lower-leg
  rotate: false
  xy: 195, 342
  size: 33, 70
  orig: 33, 70
  offset: 0, 0
  index: -1
goblingirl/left-upper-leg
  rotate: false
  xy: 37, 81
  size: 33, 70
  orig: 33, 70
  offset: 0, 0
  index: -1
goblingirl/right-upper-leg
  rotate: false
  xy: 38, 16
  size: 34, 63
  orig: 34, 63
  offset: 0, 0
  index: -1
goblin/eyes-closed
  rotate: false
  xy: 38, 2
  size: 34, 12
  orig: 34, 12
  offset: 0, 0
  index: -1
goblin/undies
  rotate: false
  xy: 54, 154
  size: 36, 29
  orig: 36, 29
  offset: 0, 0
  index: -1
goblin/right-arm
  rotate: false
  xy: 72, 102
  size: 23, 50
  orig: 23, 50
  offset: 0, 0
  index: -1
goblin/left-foot
  rotate: false
  xy: 131, 256
  size: 65, 31
  orig: 65, 31
  offset: 0, 0
  index: -1
goblingirl/right-arm
  rotate: false
  xy: 196, 290
  size: 28, 50
  orig: 28, 50
  offset: 0, 0
  index: -1
goblingirl/left-shoulder
  rotate: false
  xy: 226, 294
  size: 28, 46
  orig: 28, 46
  offset: 0, 0
  index: -1
goblin/left-arm
  rotate: false
  xy: 198, 253
  size: 37, 35
  orig: 37, 35
  offset: 0, 0
  index: -1
goblingirl/left-foot
  rotate: false
  xy: 92, 223
  size: 65, 31
  orig: 65, 31
  offset: 0, 0
  index: -1
goblingirl/right-foot
  rotate: false
  xy: 92, 188
  size: 63, 33
  orig: 63, 33
  offset: 0, 0
  index: -1
goblin/undie-straps
  rotate: false
  xy: 92, 167
  size: 55, 19
  orig: 55, 19
  offset: 0, 0
  index: -1
goblingirl/left-arm
  rotate: false
  xy: 159, 219
  size: 37, 35
  orig: 37, 35
  offset: 0, 0
  index: -1
goblin/right-shoulder
  rotate: false
  xy: 97, 120
  size: 39, 45
  orig: 39, 45
  offset: 0, 0
  index: -1
goblingirl/right-shoulder
  rotate: false
  xy: 198, 206
  size: 39, 45
  orig: 39, 45
  offset: 0, 0
  index: -1
goblin/left-hand
  rotate: false
  xy: 157, 176
  size: 36, 41
  orig: 36, 41
  offset: 0, 0
  index: -1
goblin/neck
  rotate: false
  xy: 195, 163
  size: 36, 41
  orig: 36, 41
  offset: 0, 0
  index: -1
goblingirl/undie-straps
  rotate: false
  xy: 97, 99
  size: 55, 19
  orig: 55, 19
  offset: 0, 0
  index: -1
goblingirl/neck
  rotate: false
  xy: 138, 120
  size: 35, 41
  orig: 35, 41
  offset: 0, 0
  index: -1
goblingirl/left-hand
  rotate: false
  xy: 175, 121
  size: 35, 40
  orig: 35, 40
  offset: 0, 0
  index: -1
goblin/left-shoulder
  rotate: false
  xy: 212, 117
  size: 29, 44
  orig: 29, 44
  offset: 0, 0
  index: -1
goblingirl/eyes-closed
  rotate: false
  xy: 154, 97
  size: 37, 21
  orig: 37, 21
  offset: 0, 0
  index: -1
goblin/right-hand
  rotate: false
  xy: 193, 78
  size: 36, 37
  orig: 36, 37
  offset: 0, 0
  index: -1
goblingirl/right-hand
  rotate: false
  xy: 74, 39
  size: 36, 37
  orig: 36, 37
  offset: 0, 0
  index: -1
goblingirl/undies
  rotate: false
  xy: 74, 8
  size: 36, 29
  orig: 36, 29
  offset: 0, 0
  index: -1

 

posted @ 2017-07-31 10:41 水中盗影 阅读(...) 评论(...) 编辑 收藏