GLS.AnimatedSprite.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.AnimatedSprite;
  5. (* A sprite that uses a scrolling texture for animation. *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.SysUtils,
  12. System.Math,
  13. GLS.OpenGLTokens,
  14. GLS.Scene,
  15. GLS.VectorTypes,
  16. GLS.VectorGeometry,
  17. GLS.Material,
  18. GLS.PersistentClasses,
  19. GLS.XCollection,
  20. GLS.RenderContextInfo,
  21. GLS.BaseClasses,
  22. GLS.Context,
  23. GLS.State;
  24. type
  25. TGLSpriteAnimFrame = class;
  26. TGLSpriteAnimFrameList = class;
  27. TGLSpriteAnimation = class;
  28. TGLSpriteAnimationList = class;
  29. TGLAnimatedSprite = class;
  30. (* Used by the SpriteAnimation when Dimensions are set manual. The animation
  31. will use the offsets, width and height to determine the texture coodinates for this frame. *)
  32. TGLSpriteAnimFrame = class(TXCollectionItem)
  33. private
  34. FOffsetX,
  35. FOffsetY,
  36. FWidth,
  37. FHeight: Integer;
  38. procedure DoChanged;
  39. protected
  40. procedure SetOffsetX(const Value: Integer);
  41. procedure SetOffsetY(const Value: Integer);
  42. procedure SetWidth(const Value: Integer);
  43. procedure SetHeight(const Value: Integer);
  44. procedure WriteToFiler(writer: TWriter); override;
  45. procedure ReadFromFiler(reader: TReader); override;
  46. public
  47. class function FriendlyName: string; override;
  48. class function FriendlyDescription: string; override;
  49. published
  50. property OffsetX: Integer read FOffsetX write SetOffsetX;
  51. property OffsetY: Integer read FOffsetY write SetOffsetY;
  52. property Width: Integer read FWidth write SetWidth;
  53. property Height: Integer read FHeight write SetHeight;
  54. end;
  55. TGLSpriteAnimFrameList = class(TXCollection)
  56. public
  57. constructor Create(aOwner: TPersistent); override;
  58. class function ItemsClass: TXCollectionItemClass; override;
  59. end;
  60. (* Determines if the texture coordinates are Automatically generated
  61. from the Animations properties or if they are Manually set through
  62. the Frames collection. *)
  63. TGLSpriteFrameDimensions = (sfdAuto, sfdManual);
  64. (* Used to mask the auto generated frames. The Left, Top, Right and
  65. Bottom properties determines the number of pixels to be cropped
  66. from each corresponding side of the frame. Only applicable to auto dimensions. *)
  67. TGLSpriteAnimMargins = class(TPersistent)
  68. private
  69. FOwner: TGLSpriteAnimation;
  70. FLeft, FTop, FRight, FBottom: Integer;
  71. protected
  72. procedure SetLeft(const Value: Integer);
  73. procedure SetTop(const Value: Integer);
  74. procedure SetRight(const Value: Integer);
  75. procedure SetBottom(const Value: Integer);
  76. procedure DoChanged;
  77. public
  78. constructor Create(Animation: TGLSpriteAnimation);
  79. property Owner: TGLSpriteAnimation read FOwner;
  80. published
  81. property Left: Integer read FLeft write SetLeft;
  82. property Top: Integer read FTop write SetTop;
  83. property Right: Integer read FRight write SetRight;
  84. property Bottom: Integer read FBottom write SetBottom;
  85. end;
  86. // Animations define how the texture coordinates for each offset are to be determined.
  87. TGLSpriteAnimation = class(TXCollectionItem, IGLMaterialLibrarySupported)
  88. private
  89. FCurrentFrame,
  90. FStartFrame,
  91. FEndFrame,
  92. FFrameWidth,
  93. FFrameHeight,
  94. FInterval: Integer;
  95. FFrames: TGLSpriteAnimFrameList;
  96. FLibMaterialName: TGLLibMaterialName;
  97. FLibMaterialCached: TGLLibMaterial;
  98. FDimensions: TGLSpriteFrameDimensions;
  99. FMargins: TGLSpriteAnimMargins;
  100. procedure DoChanged;
  101. protected
  102. procedure SetCurrentFrame(const Value: Integer);
  103. procedure SetFrameWidth(const Value: Integer);
  104. procedure SetFrameHeight(const Value: Integer);
  105. procedure WriteToFiler(writer: TWriter); override;
  106. procedure ReadFromFiler(reader: TReader); override;
  107. procedure SetDimensions(const Value: TGLSpriteFrameDimensions);
  108. procedure SetLibMaterialName(const val: TGLLibMaterialName);
  109. function GetLibMaterialCached: TGLLibMaterial;
  110. procedure SetInterval(const Value: Integer);
  111. procedure SetFrameRate(const Value: Single);
  112. function GetFrameRate: Single;
  113. // Implementing IGLMaterialLibrarySupported.
  114. function GetMaterialLibrary: TGLAbstractMaterialLibrary;
  115. public
  116. constructor Create(aOwner: TXCollection); override;
  117. destructor Destroy; override;
  118. class function FriendlyName: string; override;
  119. class function FriendlyDescription: string; override;
  120. property LibMaterialCached: TGLLibMaterial read GetLibMaterialCached;
  121. published
  122. // The current showing frame for this animation.
  123. property CurrentFrame: Integer read FCurrentFrame write SetCurrentFrame;
  124. // Defines the starting frame for auto dimension animations.
  125. property StartFrame: Integer read FStartFrame write FStartFrame;
  126. // Defines the ending frame for auto dimension animations.
  127. property EndFrame: Integer read FEndFrame write FEndFrame;
  128. // Width of each frame in an auto dimension animation.
  129. property FrameWidth: Integer read FFrameWidth write SetFrameWidth;
  130. // Height of each frame in an auto dimension animation.
  131. property FrameHeight: Integer read FFrameHeight write SetFrameHeight;
  132. (* The name of the lib material the sprites associated material library
  133. for this animation. *)
  134. property LibMaterialName: TGLLibMaterialName read FLibMaterialName write
  135. SetLibMaterialName;
  136. (* Manual dimension animation frames. Stores the offsets and dimensions
  137. for each frame in the animation. *)
  138. property Frames: TGLSpriteAnimFrameList read FFrames;
  139. // Automatic or manual texture coordinate generation.
  140. property Dimensions: TGLSpriteFrameDimensions read FDimensions write
  141. SetDimensions;
  142. (* The number of milliseconds between each frame in the animation.
  143. Will automatically calculate the FrameRate value when set.
  144. Will override the TGLAnimatedSprite Interval is greater than zero. *)
  145. property Interval: Integer read FInterval write SetInterval;
  146. (* The number of frames per second for the animation.
  147. Will automatically calculate the Interval value when set.
  148. Precision will depend on Interval since Interval has priority. *)
  149. property FrameRate: Single read GetFrameRate write SetFrameRate;
  150. // Sets cropping margins for auto dimension animations.
  151. property Margins: TGLSpriteAnimMargins read FMargins;
  152. end;
  153. // A collection for storing SpriteAnimation objects.
  154. TGLSpriteAnimationList = class(TXCollection)
  155. public
  156. constructor Create(aOwner: TPersistent); override;
  157. class function ItemsClass: TXCollectionItemClass; override;
  158. end;
  159. (* Sets the current animation playback mode:
  160. samNone - No playback, the animation does not progress.
  161. samPlayOnce - Plays the animation once then switches to samNone.
  162. samLoop - Play the animation forward in a continuous loop.
  163. samLoopBackward - Same as samLoop but reversed direction.
  164. samBounceForward - Plays forward and switches to samBounceBackward
  165. when EndFrame is reached.
  166. samBounceBackward - Plays backward and switches to samBounceForward
  167. when StartFrame is reached. *)
  168. TGLSpriteAnimationMode = (samNone, samPlayOnce, samLoop, samBounceForward,
  169. samBounceBackward, samLoopBackward);
  170. // An animated version for using offset texture coordinate animation.
  171. TGLAnimatedSprite = class(TGLBaseSceneObject)
  172. private
  173. FAnimations: TGLSpriteAnimationList;
  174. FMaterialLibrary: TGLMaterialLibrary;
  175. FAnimationIndex,
  176. FInterval,
  177. FRotation,
  178. FPixelRatio: Integer;
  179. FMirrorU,
  180. FMirrorV: Boolean;
  181. FAnimationMode: TGLSpriteAnimationMode;
  182. FCurrentFrameDelta: Double;
  183. FOnFrameChanged: TNotifyEvent;
  184. FOnEndFrameReached: TNotifyEvent;
  185. FOnStartFrameReached: TNotifyEvent;
  186. protected
  187. procedure DefineProperties(Filer: TFiler); override;
  188. procedure WriteAnimations(Stream: TStream);
  189. procedure ReadAnimations(Stream: TStream);
  190. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  191. procedure SetInterval(const val: Integer);
  192. procedure SetAnimationIndex(const val: Integer);
  193. procedure SetAnimationMode(const val: TGLSpriteAnimationMode);
  194. procedure SetMaterialLibrary(const val: TGLMaterialLibrary);
  195. procedure SetPixelRatio(const val: Integer);
  196. procedure SetRotation(const val: Integer);
  197. procedure SetMirrorU(const val: Boolean);
  198. procedure SetMirrorV(const val: Boolean);
  199. procedure SetFrameRate(const Value: Single);
  200. function GetFrameRate: Single;
  201. public
  202. constructor Create(AOwner: TComponent); override;
  203. destructor Destroy; override;
  204. procedure BuildList(var rci: TGLRenderContextInfo); override;
  205. procedure DoProgress(const progressTime: TGLProgressTimes); override;
  206. // Steps the current animation to the next frame
  207. procedure NextFrame;
  208. published
  209. // A collection of animations. Stores the settings for animating then sprite.
  210. property Animations: TGLSpriteAnimationList read FAnimations;
  211. // The material library that stores the lib materials for the animations.
  212. property MaterialLibrary: TGLMaterialLibrary read FMaterialLibrary write
  213. SetMaterialLibrary;
  214. (* Sets the number of milliseconds between each frame. Will recalculate
  215. the Framerate when set. Will be overridden by the TGLSpriteAnimation
  216. Interval if it is greater than zero. *)
  217. property Interval: Integer read FInterval write SetInterval;
  218. // Index of the sprite animation to be used.
  219. property AnimationIndex: Integer read FAnimationIndex write
  220. SetAnimationIndex;
  221. // Playback mode for the current animation.
  222. property AnimationMode: TGLSpriteAnimationMode read FAnimationMode write
  223. SetAnimationMode;
  224. (* Used to automatically calculate the width and height of a sprite based
  225. on the size of the frame it is showing. For example, if PixelRatio is
  226. set to 100 and the current animation frame is 100 pixels wide it will
  227. set the width of the sprite to 1. If the frame is 50 pixels width the
  228. sprite will be 0.5 wide. *)
  229. property PixelRatio: Integer read FPixelRatio write SetPixelRatio;
  230. // Rotates the sprite (in degrees).
  231. property Rotation: Integer read FRotation write SetRotation;
  232. // Mirror the generated texture coords in the U axis.
  233. property MirrorU: Boolean read FMirrorU write SetMirrorU;
  234. // Mirror the generated texture coords in the V axis.
  235. property MirrorV: Boolean read FMirrorV write SetMirrorV;
  236. (* Sets the frames per second for the current animation. Automatically
  237. calculates the Interval. Precision will be restricted to the values
  238. of Interval since Interval takes priority. *)
  239. property FrameRate: Single read GetFrameRate write SetFrameRate;
  240. property Position;
  241. property Scale;
  242. property Visible;
  243. // An event fired when the animation changes to it's next frame.
  244. property OnFrameChanged: TNotifyEvent read FOnFrameChanged write
  245. FOnFrameChanged;
  246. // An event fired when the animation reaches the end frame.
  247. property OnEndFrameReached: TNotifyEvent read FOnEndFrameReached write
  248. FOnEndFrameReached;
  249. // An event fired when the animation reaches the start frame.
  250. property OnStartFrameReached: TNotifyEvent read FOnStartFrameReached write
  251. FOnStartFrameReached;
  252. end;
  253. // -----------------------------------------------------------------------------
  254. implementation
  255. // -----------------------------------------------------------------------------
  256. // ----------
  257. // ---------- TGLSpriteAnimFrame ----------
  258. // ----------
  259. procedure TGLSpriteAnimFrame.DoChanged;
  260. begin
  261. if Assigned(Owner) then
  262. begin
  263. if Assigned(Owner.Owner) then
  264. if Owner.Owner is TGLSpriteAnimation then
  265. TGLSpriteAnimation(Owner.Owner).DoChanged;
  266. end;
  267. end;
  268. class function TGLSpriteAnimFrame.FriendlyName: string;
  269. begin
  270. Result := 'Frame';
  271. end;
  272. class function TGLSpriteAnimFrame.FriendlyDescription: string;
  273. begin
  274. Result := 'Sprite Animation Frame';
  275. end;
  276. procedure TGLSpriteAnimFrame.WriteToFiler(writer: TWriter);
  277. begin
  278. inherited;
  279. writer.WriteInteger(0); // Archive version number
  280. with writer do
  281. begin
  282. WriteInteger(OffsetX);
  283. WriteInteger(OffsetY);
  284. WriteInteger(Width);
  285. WriteInteger(Height);
  286. end;
  287. end;
  288. procedure TGLSpriteAnimFrame.ReadFromFiler(reader: TReader);
  289. var
  290. archiveVersion: Integer;
  291. begin
  292. inherited;
  293. archiveVersion := reader.ReadInteger;
  294. Assert(archiveVersion = 0);
  295. with reader do
  296. begin
  297. OffsetX := ReadInteger;
  298. OffsetY := ReadInteger;
  299. Width := ReadInteger;
  300. Height := ReadInteger;
  301. end;
  302. end;
  303. procedure TGLSpriteAnimFrame.SetOffsetX(const Value: Integer);
  304. begin
  305. if Value <> FOffsetX then
  306. begin
  307. FOffsetX := Value;
  308. DoChanged;
  309. end;
  310. end;
  311. procedure TGLSpriteAnimFrame.SetOffsetY(const Value: Integer);
  312. begin
  313. if Value <> FOffsetY then
  314. begin
  315. FOffsetY := Value;
  316. DoChanged;
  317. end;
  318. end;
  319. procedure TGLSpriteAnimFrame.SetWidth(const Value: Integer);
  320. begin
  321. if Value <> FWidth then
  322. begin
  323. FWidth := Value;
  324. DoChanged;
  325. end;
  326. end;
  327. procedure TGLSpriteAnimFrame.SetHeight(const Value: Integer);
  328. begin
  329. if Value <> FHeight then
  330. begin
  331. FHeight := Value;
  332. DoChanged;
  333. end;
  334. end;
  335. // ----------
  336. // ---------- TGLSpriteAnimFrameList ----------
  337. // ----------
  338. constructor TGLSpriteAnimFrameList.Create(aOwner: TPersistent);
  339. begin
  340. inherited;
  341. end;
  342. class function TGLSpriteAnimFrameList.ItemsClass: TXCollectionItemClass;
  343. begin
  344. Result := TGLSpriteAnimFrame;
  345. end;
  346. // ----------
  347. // ---------- TGLSpriteAnimMargins ----------
  348. // ----------
  349. constructor TGLSpriteAnimMargins.Create(Animation: TGLSpriteAnimation);
  350. begin
  351. inherited Create;
  352. FOwner := Animation;
  353. end;
  354. procedure TGLSpriteAnimMargins.SetLeft(const Value: Integer);
  355. begin
  356. if Value <> FLeft then
  357. begin
  358. FLeft := Value;
  359. DoChanged;
  360. end;
  361. end;
  362. procedure TGLSpriteAnimMargins.SetTop(const Value: Integer);
  363. begin
  364. if Value <> FTop then
  365. begin
  366. FTop := Value;
  367. DoChanged;
  368. end;
  369. end;
  370. procedure TGLSpriteAnimMargins.SetRight(const Value: Integer);
  371. begin
  372. if Value <> FRight then
  373. begin
  374. FRight := Value;
  375. DoChanged;
  376. end;
  377. end;
  378. procedure TGLSpriteAnimMargins.SetBottom(const Value: Integer);
  379. begin
  380. if Value <> FBottom then
  381. begin
  382. FBottom := Value;
  383. DoChanged;
  384. end;
  385. end;
  386. procedure TGLSpriteAnimMargins.DoChanged;
  387. begin
  388. if Assigned(Owner) then
  389. Owner.DoChanged;
  390. end;
  391. // ----------
  392. // ---------- TGLSpriteAnimation ----------
  393. // ----------
  394. constructor TGLSpriteAnimation.Create(aOwner: TXCollection);
  395. begin
  396. inherited;
  397. FFrames := TGLSpriteAnimFrameList.Create(Self);
  398. FMargins := TGLSpriteAnimMargins.Create(Self);
  399. end;
  400. destructor TGLSpriteAnimation.Destroy;
  401. begin
  402. FFrames.Free;
  403. FMargins.Free;
  404. inherited;
  405. end;
  406. function TGLSpriteAnimation.GetMaterialLibrary: TGLAbstractMaterialLibrary;
  407. begin
  408. if not (Owner is TGLSpriteAnimationList) then
  409. Result := nil
  410. else
  411. begin
  412. if not (TGLSpriteAnimationList(Owner).Owner is TGLAnimatedSprite) then
  413. Result := nil
  414. else
  415. Result :=
  416. TGLAnimatedSprite(TGLSpriteAnimationList(Owner).Owner).FMaterialLibrary;
  417. end;
  418. end;
  419. class function TGLSpriteAnimation.FriendlyName: string;
  420. begin
  421. Result := 'Animation';
  422. end;
  423. class function TGLSpriteAnimation.FriendlyDescription: string;
  424. begin
  425. Result := 'Sprite Animation';
  426. end;
  427. procedure TGLSpriteAnimation.WriteToFiler(writer: TWriter);
  428. begin
  429. inherited;
  430. writer.WriteInteger(2); // Archive version number
  431. Frames.WriteToFiler(writer);
  432. with writer do
  433. begin
  434. // Version 0
  435. WriteString(LibMaterialName);
  436. WriteInteger(CurrentFrame);
  437. WriteInteger(StartFrame);
  438. WriteInteger(EndFrame);
  439. WriteInteger(FrameWidth);
  440. WriteInteger(FrameHeight);
  441. WriteInteger(Integer(Dimensions));
  442. // Version 1
  443. WriteInteger(Interval);
  444. // Version 2
  445. WriteInteger(Margins.Left);
  446. WriteInteger(Margins.Top);
  447. WriteInteger(Margins.Right);
  448. WriteInteger(Margins.Bottom);
  449. end;
  450. end;
  451. procedure TGLSpriteAnimation.ReadFromFiler(reader: TReader);
  452. var
  453. archiveVersion: Integer;
  454. begin
  455. inherited;
  456. archiveVersion := reader.ReadInteger;
  457. Assert((archiveVersion >= 0) and (archiveVersion <= 2));
  458. Frames.ReadFromFiler(reader);
  459. with reader do
  460. begin
  461. FLibMaterialName := ReadString;
  462. CurrentFrame := ReadInteger;
  463. StartFrame := ReadInteger;
  464. EndFrame := ReadInteger;
  465. FrameWidth := ReadInteger;
  466. FrameHeight := ReadInteger;
  467. Dimensions := TGLSpriteFrameDimensions(ReadInteger);
  468. if archiveVersion >= 1 then
  469. begin
  470. Interval := ReadInteger;
  471. end;
  472. if archiveVersion >= 2 then
  473. begin
  474. Margins.Left := ReadInteger;
  475. Margins.Top := ReadInteger;
  476. Margins.Right := ReadInteger;
  477. Margins.Bottom := ReadInteger;
  478. end;
  479. end;
  480. end;
  481. procedure TGLSpriteAnimation.DoChanged;
  482. begin
  483. if Assigned(Owner) then
  484. begin
  485. if Assigned(Owner.Owner) then
  486. if Owner.Owner is TGLBaseSceneObject then
  487. TGLBaseSceneObject(Owner.Owner).NotifyChange(Self);
  488. end;
  489. end;
  490. procedure TGLSpriteAnimation.SetCurrentFrame(const Value: Integer);
  491. begin
  492. if Value <> FCurrentFrame then
  493. begin
  494. FCurrentFrame := Value;
  495. if FCurrentFrame < 0 then
  496. FCurrentFrame := -1;
  497. DoChanged;
  498. end;
  499. end;
  500. procedure TGLSpriteAnimation.SetFrameWidth(const Value: Integer);
  501. begin
  502. if Value <> FFrameWidth then
  503. begin
  504. FFrameWidth := Value;
  505. DoChanged;
  506. end;
  507. end;
  508. procedure TGLSpriteAnimation.SetFrameHeight(const Value: Integer);
  509. begin
  510. if Value <> FFrameHeight then
  511. begin
  512. FFrameHeight := Value;
  513. DoChanged;
  514. end;
  515. end;
  516. procedure TGLSpriteAnimation.SetDimensions(
  517. const Value: TGLSpriteFrameDimensions);
  518. begin
  519. if Value <> FDimensions then
  520. begin
  521. FDimensions := Value;
  522. DoChanged;
  523. end;
  524. end;
  525. procedure TGLSpriteAnimation.SetLibMaterialName(const val: TGLLibMaterialName);
  526. begin
  527. if val <> FLibMaterialName then
  528. begin
  529. FLibMaterialName := val;
  530. FLibMaterialCached := nil;
  531. end;
  532. end;
  533. function TGLSpriteAnimation.GetLibMaterialCached: TGLLibMaterial;
  534. begin
  535. Result := nil;
  536. if FLibMaterialName = '' then
  537. exit;
  538. if not Assigned(FLibMaterialCached) then
  539. if Assigned(Owner) then
  540. if Assigned(Owner.Owner) then
  541. if Owner.Owner is TGLAnimatedSprite then
  542. if Assigned(TGLAnimatedSprite(Owner.Owner).MaterialLibrary) then
  543. FLibMaterialCached :=
  544. TGLAnimatedSprite(Owner.Owner).MaterialLibrary.Materials.GetLibMaterialByName(FLibMaterialName);
  545. Result := FLibMaterialCached;
  546. end;
  547. procedure TGLSpriteAnimation.SetInterval(const Value: Integer);
  548. begin
  549. if Value <> FInterval then
  550. begin
  551. FInterval := Value;
  552. DoChanged;
  553. end;
  554. end;
  555. procedure TGLSpriteAnimation.SetFrameRate(const Value: Single);
  556. begin
  557. if Value > 0 then
  558. Interval := Round(1000 / Value)
  559. else
  560. Interval := 0;
  561. end;
  562. function TGLSpriteAnimation.GetFrameRate: Single;
  563. begin
  564. if Interval > 0 then
  565. Result := 1000 / Interval
  566. else
  567. Result := 0;
  568. end;
  569. // ----------
  570. // ---------- TGLSpriteAnimationList ----------
  571. // ----------
  572. constructor TGLSpriteAnimationList.Create(aOwner: TPersistent);
  573. begin
  574. inherited;
  575. end;
  576. class function TGLSpriteAnimationList.ItemsClass: TXCollectionItemClass;
  577. begin
  578. Result := TGLSpriteAnimation;
  579. end;
  580. // ----------
  581. // ---------- TGLAnimatedSprite ----------
  582. // ----------
  583. constructor TGLAnimatedSprite.Create(AOwner: TComponent);
  584. begin
  585. inherited;
  586. FAnimations := TGLSpriteAnimationList.Create(Self);
  587. FAnimationIndex := -1;
  588. FInterval := 100;
  589. FPixelRatio := 100;
  590. FRotation := 0;
  591. FMirrorU := False;
  592. FMirrorV := False;
  593. ObjectStyle := [osDirectDraw];
  594. end;
  595. destructor TGLAnimatedSprite.Destroy;
  596. begin
  597. FAnimations.Free;
  598. inherited;
  599. end;
  600. {$WARNINGS Off}
  601. procedure TGLAnimatedSprite.BuildList(var rci: TGLRenderContextInfo);
  602. var
  603. vx, vy: TAffineVector;
  604. w, h, temp: Single;
  605. mat: TGLMatrix;
  606. u0, v0, u1, v1: Single;
  607. x0, y0, x1, y1, TexWidth, TexHeight: Integer;
  608. Anim: TGLSpriteAnimation;
  609. Frame: TGLSpriteAnimFrame;
  610. libMat: TGLLibMaterial;
  611. IsAuto: Boolean;
  612. begin
  613. if (FAnimationIndex <> -1) and (FAnimationIndex < Animations.Count) then
  614. begin
  615. Anim := TGLSpriteAnimation(Animations[FAnimationIndex]);
  616. if (Anim.CurrentFrame >= 0) then
  617. begin
  618. if (Anim.Dimensions = sfdManual) and (Anim.CurrentFrame <
  619. Anim.Frames.Count) then
  620. Frame := TGLSpriteAnimFrame(Anim.Frames[Anim.CurrentFrame])
  621. else
  622. Frame := nil;
  623. IsAuto := (Anim.CurrentFrame <= Anim.EndFrame) and
  624. (Anim.CurrentFrame >= Anim.StartFrame) and
  625. (Anim.Dimensions = sfdAuto);
  626. if Assigned(Frame) or IsAuto then
  627. begin
  628. libMat := Anim.LibMaterialCached;
  629. h := 0.5;
  630. w := 0.5;
  631. u0 := 0;
  632. v0 := 0;
  633. u1 := 0;
  634. v1 := 0;
  635. if Assigned(libMat) then
  636. begin
  637. TexWidth := libMat.Material.Texture.Image.Width;
  638. TexHeight := libMat.Material.Texture.Image.Height;
  639. if Anim.Dimensions = sfdManual then
  640. begin
  641. x0 := Frame.OffsetX;
  642. y0 := Frame.OffsetY;
  643. x1 := x0 + Frame.Width - 1;
  644. y1 := y0 + Frame.Height - 1;
  645. end
  646. else
  647. begin
  648. if (TexWidth > 0) and (Anim.FrameWidth > 0)
  649. and (TexHeight > 0) and (Anim.FrameHeight > 0) then
  650. begin
  651. x0 := Anim.FrameWidth * (Anim.CurrentFrame mod (TexWidth div
  652. Anim.FrameWidth));
  653. y0 := Anim.FrameHeight * (Anim.CurrentFrame div (TexWidth div
  654. Anim.FrameWidth));
  655. end
  656. else
  657. begin
  658. x0 := 0;
  659. y0 := 0;
  660. end;
  661. x1 := x0 + Anim.FrameWidth - 1;
  662. y1 := y0 + Anim.FrameHeight - 1;
  663. x0 := x0 + Anim.Margins.Left;
  664. y0 := y0 + Anim.Margins.Top;
  665. x1 := x1 - Anim.Margins.Right;
  666. y1 := y1 - Anim.Margins.Bottom;
  667. end;
  668. if (TexWidth > 0) and (TexHeight > 0) and (x0 <> x1) and (y0 <> y1)
  669. then
  670. begin
  671. u0 := x0 / TexWidth;
  672. v0 := 1 - y1 / TexHeight;
  673. u1 := x1 / TexWidth;
  674. v1 := 1 - y0 / TexHeight;
  675. w := 0.5 * (x1 - x0) / FPixelRatio;
  676. h := 0.5 * (y1 - y0) / FPixelRatio;
  677. end;
  678. end;
  679. gl.GetFloatv(GL_MODELVIEW_MATRIX, @mat);
  680. vx.X := mat.V[0].X;
  681. vy.X := mat.V[0].Y;
  682. vx.Y := mat.V[1].X;
  683. vy.Y := mat.V[1].Y;
  684. vx.Z := mat.V[2].X;
  685. vy.Z := mat.V[2].Y;
  686. ScaleVector(vx, w * VectorLength(vx));
  687. ScaleVector(vy, h * VectorLength(vy));
  688. if FMirrorU then
  689. begin
  690. temp := u0;
  691. u0 := u1;
  692. u1 := temp;
  693. end;
  694. if FMirrorV then
  695. begin
  696. temp := v0;
  697. v0 := v1;
  698. v1 := temp;
  699. end;
  700. if Assigned(libMat) then
  701. libMat.Apply(rci);
  702. rci.GLStates.Disable(stLighting);
  703. if FRotation <> 0 then
  704. begin
  705. gl.MatrixMode(GL_MODELVIEW);
  706. gl.PushMatrix;
  707. gl.Rotatef(FRotation, mat.V[0].Z, mat.V[1].Z, mat.V[2].Z);
  708. end;
  709. gl.Begin_(GL_QUADS);
  710. gl.TexCoord2f(u1, v1);
  711. gl.Vertex3f(vx.X + vy.X, vx.Y + vy.Y,
  712. vx.Z + vy.Z);
  713. gl.TexCoord2f(u0, v1);
  714. gl.Vertex3f(-vx.X + vy.X,
  715. -vx.Y + vy.Y,
  716. -vx.Z + vy.Z);
  717. gl.TexCoord2f(u0, v0);
  718. gl.Vertex3f(-vx.X - vy.X, -vx.Y - vy.Y, -vx.Z - vy.Z);
  719. gl.TexCoord2f(u1, v0);
  720. gl.Vertex3f(vx.X - vy.X, vx.Y - vy.Y, vx.Z - vy.Z);
  721. gl.End_;
  722. if FRotation <> 0 then
  723. begin
  724. gl.PopMatrix;
  725. end;
  726. if Assigned(libMat) then
  727. libMat.UnApply(rci);
  728. end;
  729. end;
  730. end;
  731. end;
  732. {$WARNINGS On}
  733. procedure TGLAnimatedSprite.DoProgress(const progressTime: TGLProgressTimes);
  734. var
  735. i, intr: Integer;
  736. begin
  737. inherited;
  738. if (AnimationIndex = -1) then
  739. exit;
  740. intr := TGLSpriteAnimation(Animations[AnimationIndex]).Interval;
  741. if intr = 0 then
  742. intr := Interval;
  743. if (FAnimationMode <> samNone) and (intr > 0) then
  744. begin
  745. FCurrentFrameDelta := FCurrentFrameDelta + (progressTime.deltaTime * 1000) /
  746. intr;
  747. if FCurrentFrameDelta >= 1 then
  748. begin
  749. for i := 0 to Floor(FCurrentFrameDelta) - 1 do
  750. begin
  751. NextFrame;
  752. FCurrentFrameDelta := FCurrentFrameDelta - 1;
  753. end;
  754. end;
  755. end;
  756. end;
  757. procedure TGLAnimatedSprite.Notification(AComponent: TComponent; Operation:
  758. TOperation);
  759. begin
  760. if (Operation = opRemove) and (AComponent = FMaterialLibrary) then
  761. FMaterialLibrary := nil;
  762. inherited;
  763. end;
  764. procedure TGLAnimatedSprite.DefineProperties(Filer: TFiler);
  765. begin
  766. inherited;
  767. Filer.DefineBinaryProperty('SpriteAnimations',
  768. ReadAnimations, WriteAnimations,
  769. FAnimations.Count > 0);
  770. end;
  771. procedure TGLAnimatedSprite.WriteAnimations(Stream: TStream);
  772. var
  773. writer: TWriter;
  774. begin
  775. writer := TWriter.Create(stream, 16384);
  776. try
  777. Animations.WriteToFiler(writer);
  778. finally
  779. writer.Free;
  780. end;
  781. end;
  782. procedure TGLAnimatedSprite.ReadAnimations(Stream: TStream);
  783. var
  784. reader: TReader;
  785. begin
  786. reader := TReader.Create(stream, 16384);
  787. try
  788. Animations.ReadFromFiler(reader);
  789. finally
  790. reader.Free;
  791. end;
  792. end;
  793. procedure TGLAnimatedSprite.NextFrame;
  794. var
  795. currentFrame,
  796. startFrame,
  797. endFrame: Integer;
  798. Anim: TGLSpriteAnimation;
  799. begin
  800. if (FAnimationIndex = -1) or (FAnimationIndex >= Animations.Count) then
  801. exit;
  802. Anim := TGLSpriteAnimation(Animations[FAnimationIndex]);
  803. currentFrame := Anim.CurrentFrame;
  804. if Anim.Dimensions = sfdManual then
  805. begin
  806. startFrame := 0;
  807. endFrame := Anim.Frames.Count - 1
  808. end
  809. else
  810. begin
  811. startFrame := Anim.StartFrame;
  812. endFrame := Anim.EndFrame;
  813. end;
  814. case AnimationMode of
  815. samLoop, samBounceForward, samPlayOnce:
  816. begin
  817. if (currentFrame = endFrame) and Assigned(FOnEndFrameReached) then
  818. FOnEndFrameReached(Self);
  819. Inc(currentFrame);
  820. end;
  821. samBounceBackward, samLoopBackward:
  822. begin
  823. if (currentFrame = startFrame) and Assigned(FOnStartFrameReached) then
  824. FOnStartFrameReached(Self);
  825. Dec(CurrentFrame);
  826. end;
  827. end;
  828. if (AnimationMode <> samNone) and Assigned(FOnFrameChanged) then
  829. FOnFrameChanged(Self);
  830. case AnimationMode of
  831. samPlayOnce:
  832. begin
  833. if currentFrame > endFrame then
  834. AnimationMode := samNone;
  835. end;
  836. samLoop:
  837. begin
  838. if currentFrame > endFrame then
  839. currentFrame := startFrame;
  840. end;
  841. samBounceForward:
  842. begin
  843. if currentFrame = endFrame then
  844. AnimationMode := samBounceBackward;
  845. end;
  846. samLoopBackward:
  847. begin
  848. if currentFrame < startFrame then
  849. CurrentFrame := endFrame;
  850. end;
  851. samBounceBackward:
  852. begin
  853. if currentFrame = startFrame then
  854. AnimationMode := samBounceForward;
  855. end;
  856. end;
  857. Anim.CurrentFrame := currentFrame;
  858. end;
  859. procedure TGLAnimatedSprite.SetInterval(const val: Integer);
  860. begin
  861. if val <> FInterval then
  862. begin
  863. FInterval := val;
  864. NotifyChange(Self);
  865. end;
  866. end;
  867. procedure TGLAnimatedSprite.SetFrameRate(const Value: Single);
  868. begin
  869. if Value > 0 then
  870. Interval := Round(1000 / Value)
  871. else
  872. Interval := 0;
  873. end;
  874. function TGLAnimatedSprite.GetFrameRate: Single;
  875. begin
  876. if Interval > 0 then
  877. Result := 1000 / Interval
  878. else
  879. Result := 0;
  880. end;
  881. procedure TGLAnimatedSprite.SetAnimationIndex(const val: Integer);
  882. begin
  883. if val <> FAnimationIndex then
  884. begin
  885. FAnimationIndex := val;
  886. if FAnimationIndex < 0 then
  887. FAnimationIndex := -1;
  888. if (FAnimationIndex <> -1) and (FAnimationIndex < Animations.Count) then
  889. with TGLSpriteAnimation(Animations[FAnimationIndex]) do
  890. case AnimationMode of
  891. samNone, samPlayOnce, samLoop, samBounceForward:
  892. CurrentFrame := StartFrame;
  893. samLoopBackward, samBounceBackward:
  894. CurrentFrame := EndFrame;
  895. end;
  896. NotifyChange(Self);
  897. end;
  898. end;
  899. procedure TGLAnimatedSprite.SetAnimationMode(const val: TGLSpriteAnimationMode);
  900. begin
  901. if val <> FAnimationMode then
  902. begin
  903. FAnimationMode := val;
  904. NotifyChange(Self);
  905. end;
  906. end;
  907. procedure TGLAnimatedSprite.SetMaterialLibrary(const val: TGLMaterialLibrary);
  908. var
  909. i: Integer;
  910. begin
  911. if val <> FMaterialLibrary then
  912. begin
  913. if FMaterialLibrary <> nil then
  914. FMaterialLibrary.RemoveFreeNotification(Self);
  915. FMaterialLibrary := val;
  916. if FMaterialLibrary <> nil then
  917. FMaterialLibrary.FreeNotification(Self);
  918. for i := 0 to Animations.Count - 1 do
  919. TGLSpriteAnimation(Animations[i]).FLibMaterialCached := nil;
  920. NotifyChange(Self);
  921. end;
  922. end;
  923. procedure TGLAnimatedSprite.SetPixelRatio(const val: Integer);
  924. begin
  925. if (FPixelRatio <> val) and (val > 0) then
  926. begin
  927. FPixelRatio := val;
  928. NotifyChange(Self);
  929. end;
  930. end;
  931. procedure TGLAnimatedSprite.SetRotation(const val: Integer);
  932. begin
  933. if val <> FRotation then
  934. begin
  935. FRotation := val;
  936. NotifyChange(Self);
  937. end;
  938. end;
  939. procedure TGLAnimatedSprite.SetMirrorU(const val: Boolean);
  940. begin
  941. if val <> FMirrorU then
  942. begin
  943. FMirrorU := val;
  944. NotifyChange(Self);
  945. end;
  946. end;
  947. procedure TGLAnimatedSprite.SetMirrorV(const val: Boolean);
  948. begin
  949. if val <> FMirrorV then
  950. begin
  951. FMirrorV := val;
  952. NotifyChange(Self);
  953. end;
  954. end;
  955. // -----------------------------------------------------------------------------
  956. initialization
  957. // -----------------------------------------------------------------------------
  958. RegisterClasses([TGLAnimatedSprite,
  959. TGLSpriteAnimFrame, TGLSpriteAnimFrameList,
  960. TGLSpriteAnimation, TGLSpriteAnimationList]);
  961. RegisterXCollectionItemClass(TGLSpriteAnimFrame);
  962. RegisterXCollectionItemClass(TGLSpriteAnimation);
  963. finalization
  964. UnregisterXCollectionItemClass(TGLSpriteAnimFrame);
  965. UnregisterXCollectionItemClass(TGLSpriteAnimation);
  966. end.