// ////////////////////////////////////////////////////////////////////////////// // Generic delphi runtime v3.6 for Spine animation tool // // Runtime port by cjk (hzi1980@163.com) // // ///////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// //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