GXS.AnimatedSprite.pas 29 KB

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