AsphyreBillboards.pas
上传用户:ctlcnc
上传日期:2021-12-10
资源大小:4933k
文件大小:19k
源码类别:

2D图形编程

开发平台:

Delphi

  1. unit AsphyreBillboards;
  2. //---------------------------------------------------------------------------
  3. // AsphyreBillboards.pas                                Modified: 10-Mar-2007
  4. // Asphyre billboards                                             Version 1.0
  5. //---------------------------------------------------------------------------
  6. // Important Notice:
  7. //
  8. // If you modify/use this code or one of its parts either in original or
  9. // modified form, you must comply with Mozilla Public License v1.1,
  10. // specifically section 3, "Distribution Obligations". Failure to do so will
  11. // result in the license breach, which will be resolved in the court.
  12. // Remember that violating author's rights is considered a serious crime in
  13. // many countries. Thank you!
  14. //
  15. // !! Please *read* Mozilla Public License 1.1 document located at:
  16. //  http://www.mozilla.org/MPL/
  17. //
  18. // If you require any clarifications about the license, feel free to contact
  19. // us or post your question on our forums at: http://www.afterwarp.net
  20. //---------------------------------------------------------------------------
  21. // The contents of this file are subject to the Mozilla Public License
  22. // Version 1.1 (the "License"); you may not use this file except in
  23. // compliance with the License. You may obtain a copy of the License at
  24. // http://www.mozilla.org/MPL/
  25. //
  26. // Software distributed under the License is distributed on an "AS IS"
  27. // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  28. // License for the specific language governing rights and limitations
  29. // under the License.
  30. //
  31. // The Original Code is AsphyreBillboards.pas.
  32. //
  33. // The Initial Developer of the Original Code is M. Sc. Yuriy Kotsarenko.
  34. // Portions created by M. Sc. Yuriy Kotsarenko are Copyright (C) 2007,
  35. // Afterwarp Interactive. All Rights Reserved.
  36. //---------------------------------------------------------------------------
  37. interface
  38. //---------------------------------------------------------------------------
  39. uses
  40.  Windows, Direct3D9, D3DX9, Vectors2, Vectors3, Vectors4, Matrices4,
  41.  AsphyreTypes, AsphyreUtils, AsphyreDevices, AsphyreEvents, AsphyreEffects,
  42.  AsphyreTextures, AsphyreImages, BBTypes;
  43. //---------------------------------------------------------------------------
  44. const
  45.  // The following parameters highly affect the rendering performance. The
  46.  // higher values means that more primitives will fit in cache, but it will
  47.  // also occupy more bandwidth, even when few primitives are rendered.
  48.  //
  49.  // These parameters can be fine-tuned in a finished product to improve the
  50.  // overall performance.
  51.  MaxCachedPrimitives = 4096;
  52.  MaxCachedIndexes    = 8192;
  53.  MaxCachedVertices   = 8192;
  54. //---------------------------------------------------------------------------
  55. type
  56.  TBillboardPoints = array[0..3] of TVector4;
  57. //---------------------------------------------------------------------------
  58.  TAsphyreBB = class
  59.  private
  60.   FDevice   : TAsphyreDevice;
  61.   FBBList   : TAsphyreBillboards;
  62.   PosArray  : TVectors4;
  63.   DepthOrder: array of Integer;
  64.   VertexBuffer: IDirect3DVertexBuffer9;
  65.   IndexBuffer : IDirect3DIndexBuffer9;
  66.   VertexArray : Pointer;
  67.   IndexArray  : Pointer;
  68.   FVertexCache: Integer;
  69.   FIndexCache : Integer;
  70.   FVertexCount: Integer;
  71.   FIndexCount : Integer;
  72.   FPrimitives   : Integer;
  73.   FMaxPrimitives: Integer;
  74.   FCacheStall : Integer;
  75.   CachedDrawFx: Integer;
  76.   CachedTex   : TAsphyreCustomTexture;
  77.   procedure InitCacheSpec();
  78.   procedure CreateStaticObjects();
  79.   procedure DestroyStaticObjects();
  80.   procedure OnDeviceCreate(Sender: TObject; EventParam: Pointer;
  81.    var Success: Boolean); virtual;
  82.   procedure OnDeviceDestroy(Sender: TObject; EventParam: Pointer;
  83.    var Success: Boolean); virtual;
  84.   function CreateDynamicBuffers(): Boolean;
  85.   procedure DestroyDynamicBuffers();
  86.   procedure ResetDeviceStates();
  87.   procedure OnDeviceReset(Sender: TObject; EventParam: Pointer;
  88.    var Success: Boolean); virtual;
  89.   procedure OnDeviceLost(Sender: TObject; EventParam: Pointer;
  90.    var Success: Boolean); virtual;
  91.   function UploadVertexBuffer(): Boolean;
  92.   function UploadIndexBuffer(): Boolean;
  93.   function PrepareDraw(): Boolean;
  94.   function BufferDraw(): Boolean;
  95.   procedure RequestCache(Vertices, Indices, DrawFx: Integer;
  96.    ReqTex: TAsphyreCustomTexture);
  97.   function NextVertexEntry(): Pointer;
  98.   procedure AddIndexEntry(Index: Integer);
  99.   procedure ResetCache();
  100.   procedure MakeDepthOrder();
  101.   procedure SortDepthOrder(Left, Right: Integer);
  102.   procedure AddToCache(const Pos: TVector4; const Points: TPoint4;
  103.    const Mapping: TPoint4; Color: Cardinal; Tex: TAsphyreCustomTexture;
  104.    DrawFx: Integer);
  105.  public
  106.   property Device: TAsphyreDevice read FDevice;
  107.   property BBList: TAsphyreBillboards read FBBList;
  108.   // Indicates how many times the buffer has been flushed out inside current
  109.   // scene block.
  110.   property CacheStall: Integer read FCacheStall;
  111.   procedure Draw(const Pos: TVector3; const Size: TPoint2; Phi: Single;
  112.    Color: Cardinal; Image: TAsphyreCustomImage; Pattern: Integer;
  113.    DrawFx: Integer);
  114.   procedure Render(ViewMtx, ProjMtx: PMatrix4);
  115.   constructor Create(ADevice: TAsphyreDevice);
  116.   destructor Destroy(); override;
  117.  end;
  118. //---------------------------------------------------------------------------
  119. implementation
  120. //----------------------------------------------------------------------------
  121. const
  122.  VertexType = D3DFVF_XYZ or D3DFVF_DIFFUSE or D3DFVF_TEX1;
  123. //--------------------------------------------------------------------------
  124. type
  125. //--------------------------------------------------------------------------
  126.  PVertexRecord = ^TVertexRecord;
  127.  TVertexRecord = record
  128.   Vertex  : TVector3;
  129.   Diffuse : Longword;
  130.   TexCoord: TPoint2;
  131.  end;
  132. //---------------------------------------------------------------------------
  133. constructor TAsphyreBB.Create(ADevice: TAsphyreDevice);
  134. begin
  135.  inherited Create();
  136.  FDevice:= ADevice;
  137.  FBBList:= TAsphyreBillboards.Create();
  138.  PosArray:= TVectors4.Create();
  139.  EventDeviceCreate.Subscribe(OnDeviceCreate, FDevice);
  140.  EventDeviceDestroy.Subscribe(OnDeviceDestroy, FDevice);
  141.  EventDeviceReset.Subscribe(OnDeviceReset, FDevice);
  142.  EventDeviceLost.Subscribe(OnDeviceLost, FDevice);
  143.  VertexArray := nil;
  144.  IndexArray  := nil;
  145.  VertexBuffer:= nil;
  146.  IndexBuffer := nil;
  147. end;
  148. //---------------------------------------------------------------------------
  149. destructor TAsphyreBB.Destroy();
  150. begin
  151.  DestroyDynamicBuffers();
  152.  DestroyStaticObjects();
  153.  EventDeviceLost.Unsubscribe(OnDeviceLost);
  154.  EventDeviceReset.Unsubscribe(OnDeviceReset);
  155.  EventDeviceDestroy.Unsubscribe(OnDeviceDestroy);
  156.  EventDeviceCreate.Unsubscribe(OnDeviceCreate);
  157.  PosArray.Free();
  158.  FBBList.Free();
  159.  inherited;
  160. end;
  161. //---------------------------------------------------------------------------
  162. procedure TAsphyreBB.InitCacheSpec();
  163. begin
  164.  with FDevice.Caps9 do
  165.   begin
  166.    FMaxPrimitives:= Min2(MaxPrimitiveCount, MaxCachedPrimitives);
  167.    FVertexCache:= Min2(MaxVertexIndex, MaxCachedVertices);
  168.    FIndexCache:= Min2(MaxVertexIndex, MaxCachedIndexes);
  169.   end;
  170. end;
  171. //---------------------------------------------------------------------------
  172. procedure TAsphyreBB.CreateStaticObjects();
  173. begin
  174.  ReallocMem(VertexArray, FVertexCache * SizeOf(TVertexRecord));
  175.  FillChar(VertexArray^, FVertexCache * SizeOf(TVertexRecord), 0);
  176.  ReallocMem(IndexArray, FIndexCache * SizeOf(Word));
  177.  FillChar(IndexArray^, FIndexCache * SizeOf(Word), 0);
  178. end;
  179. //---------------------------------------------------------------------------
  180. procedure TAsphyreBB.DestroyStaticObjects();
  181. begin
  182.  if (IndexArray <> nil) then
  183.   begin
  184.    FreeMem(IndexArray);
  185.    IndexArray:= nil;
  186.   end;
  187.  if (VertexArray <> nil) then
  188.   begin
  189.    FreeMem(VertexArray);
  190.    VertexArray:= nil;
  191.   end;
  192. end;
  193. //---------------------------------------------------------------------------
  194. procedure TAsphyreBB.OnDeviceCreate(Sender: TObject; EventParam: Pointer;
  195.  var Success: Boolean);
  196. begin
  197.  Success:= (FDevice <> nil)and(FDevice is TAsphyreDevice);
  198.  if (Success) then
  199.   begin
  200.    InitCacheSpec();
  201.    CreateStaticObjects();
  202.   end;
  203. end;
  204. //--------------------------------------------------------------------------
  205. function TAsphyreBB.CreateDynamicBuffers(): Boolean;
  206. begin
  207.  // -> Dynamic Vertex Buffer
  208.  Result:= Succeeded(TAsphyreDevice(FDevice).Dev9.CreateVertexBuffer(FVertexCache *
  209.   SizeOf(TVertexRecord), D3DUSAGE_WRITEONLY or D3DUSAGE_DYNAMIC, VertexType,
  210.   D3DPOOL_DEFAULT, VertexBuffer, nil));
  211.  if (not Result) then Exit;
  212.  // -> Dynamic Index Buffer
  213.  Result:= Succeeded(TAsphyreDevice(FDevice).Dev9.CreateIndexBuffer(FIndexCache *
  214.   SizeOf(Word), D3DUSAGE_WRITEONLY or D3DUSAGE_DYNAMIC, D3DFMT_INDEX16,
  215.   D3DPOOL_DEFAULT, IndexBuffer, nil));
  216. end;
  217. //---------------------------------------------------------------------------
  218. procedure TAsphyreBB.DestroyDynamicBuffers();
  219. begin
  220.  if (IndexBuffer <> nil) then IndexBuffer:= nil;
  221.  if (VertexBuffer <> nil) then VertexBuffer:= nil;
  222. end;
  223. //---------------------------------------------------------------------------
  224. procedure TAsphyreBB.ResetDeviceStates();
  225. begin
  226.  FVertexCount:= 0;
  227.  FIndexCount := 0;
  228.  FPrimitives := 0;
  229.  CachedDrawFx:= fxUndefined;
  230.  CachedTex   := nil;
  231.  FCacheStall:= 0;
  232. end;
  233. //---------------------------------------------------------------------------
  234. procedure TAsphyreBB.OnDeviceDestroy(Sender: TObject; EventParam: Pointer;
  235.  var Success: Boolean);
  236. begin
  237.  DestroyStaticObjects();
  238. end;
  239. //---------------------------------------------------------------------------
  240. procedure TAsphyreBB.OnDeviceReset(Sender: TObject; EventParam: Pointer;
  241.  var Success: Boolean);
  242. begin
  243.  Success:= CreateDynamicBuffers();
  244.  if (Success) then ResetDeviceStates();
  245. end;
  246. //---------------------------------------------------------------------------
  247. procedure TAsphyreBB.OnDeviceLost(Sender: TObject; EventParam: Pointer;
  248.  var Success: Boolean);
  249. begin
  250.  DestroyDynamicBuffers();
  251. end;
  252. //---------------------------------------------------------------------------
  253. function TAsphyreBB.UploadVertexBuffer(): Boolean;
  254. var
  255.  MemAddr: Pointer;
  256.  BufSize: Integer;
  257. begin
  258.  BufSize:= FVertexCount * SizeOf(TVertexRecord);
  259.  Result:= Succeeded(VertexBuffer.Lock(0, BufSize, MemAddr, D3DLOCK_DISCARD));
  260.  if (Result) then
  261.   begin
  262.    Move(VertexArray^, MemAddr^, BufSize);
  263.    Result:= Succeeded(VertexBuffer.Unlock());
  264.   end;
  265. end;
  266. //---------------------------------------------------------------------------
  267. function TAsphyreBB.UploadIndexBuffer(): Boolean;
  268. var
  269.  MemAddr: Pointer;
  270.  BufSize: Integer;
  271. begin
  272.  BufSize:= FIndexCount * SizeOf(Word);
  273.  Result:= Succeeded(IndexBuffer.Lock(0, BufSize, MemAddr, D3DLOCK_DISCARD));
  274.  if (Result) then
  275.   begin
  276.    Move(IndexArray^, MemAddr^, BufSize);
  277.    Result:= Succeeded(IndexBuffer.Unlock());
  278.   end;
  279. end;
  280. //---------------------------------------------------------------------------
  281. function TAsphyreBB.PrepareDraw(): Boolean;
  282. begin
  283.  with TAsphyreDevice(FDevice).Dev9 do
  284.   begin
  285.    // (1) Use our vertex buffer for displaying primitives.
  286.    Result:= Succeeded(SetStreamSource(0, VertexBuffer, 0,
  287.     SizeOf(TVertexRecord)));
  288.    // (2) Use our index buffer to indicate the vertices of our primitives.
  289.    if (Result) then
  290.     Result:= Succeeded(SetIndices(IndexBuffer));
  291.    // (3) Disable vertex shader.
  292.    if (Result) then
  293.     Result:= Succeeded(SetVertexShader(nil));
  294.    // (4) Set the flexible vertex format of our vertex buffer.
  295.    if (Result) then
  296.     Result:= Succeeded(SetFVF(VertexType));
  297.   end;
  298. end;
  299. //---------------------------------------------------------------------------
  300. function TAsphyreBB.BufferDraw(): Boolean;
  301. begin
  302.  with FDevice.Dev9 do
  303.   begin
  304.    Result:= Succeeded(DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0,
  305.     FVertexCount, 0, FPrimitives));
  306.   end;
  307. end;
  308. //---------------------------------------------------------------------------
  309. procedure TAsphyreBB.ResetCache();
  310. begin
  311.  if (FVertexCount > 0)and(FPrimitives > 0) then
  312.   begin
  313.    if (UploadVertexBuffer())and(UploadIndexBuffer())and(PrepareDraw()) then
  314.     BufferDraw();
  315.    Inc(FCacheStall);
  316.   end;
  317.  FVertexCount:= 0;
  318.  FIndexCount := 0;
  319.  FPrimitives := 0;
  320. end;
  321. //---------------------------------------------------------------------------
  322. procedure TAsphyreBB.RequestCache(Vertices, Indices, DrawFx: Integer;
  323.  ReqTex: TAsphyreCustomTexture);
  324. var
  325.  NeedReset: Boolean;
  326. begin
  327.  NeedReset:= (FVertexCount + Vertices > FVertexCache);
  328.  NeedReset:= NeedReset or (FIndexCount + Indices > FIndexCache);
  329.  NeedReset:= NeedReset or (CachedDrawFx <> DrawFx) or (CachedTex <> ReqTex);
  330.  if (NeedReset) then
  331.   begin
  332.    ResetCache();
  333.    if (CachedTex <> ReqTex) then
  334.     with FDevice.Dev9 do
  335.      begin
  336.       if (ReqTex <> nil) then ReqTex.Activate(0) else SetTexture(0, nil);
  337.       CachedTex:= ReqTex;
  338.      end;
  339.    if (CachedDrawFx = fxUndefined)or(CachedDrawFx <> DrawFx) then
  340.     EffectManager.HandleCode(Self, TAsphyreDevice(FDevice).Dev9, DrawFx);
  341.    CachedDrawFx:= DrawFx;
  342.   end;
  343. end;
  344. //---------------------------------------------------------------------------
  345. function TAsphyreBB.NextVertexEntry(): Pointer;
  346. begin
  347.  Result:= Pointer(Integer(VertexArray) + (FVertexCount * SizeOf(TVertexRecord)));
  348. end;
  349. //---------------------------------------------------------------------------
  350. procedure TAsphyreBB.AddIndexEntry(Index: Integer);
  351. var
  352.  Entry: PWord;
  353. begin
  354.  Entry:= Pointer(Integer(IndexArray) + (FIndexCount * SizeOf(Word)));
  355.  Entry^:= Index;
  356.  Inc(FIndexCount);
  357. end;
  358. //---------------------------------------------------------------------------
  359. procedure TAsphyreBB.Draw(const Pos: TVector3; const Size: TPoint2;
  360.  Phi: Single; Color: Cardinal; Image: TAsphyreCustomImage; Pattern: Integer;
  361.  DrawFx: Integer);
  362. var
  363.  Item: TAsphyreBillboard;
  364. begin
  365.  Item.Size   := Size;
  366.  Item.Color  := Color;
  367.  Item.DrawFx := DrawFx;
  368.  Item.Image  := Image;
  369.  Item.Pattern:= Pattern;
  370.  Item.Phi    := Phi;
  371.  FBBList.Insert(Item);
  372.  PosArray.Add(Pos);
  373. end;
  374. //---------------------------------------------------------------------------
  375. procedure TAsphyreBB.SortDepthOrder(Left, Right: Integer);
  376. var
  377.  Lo, Hi: Integer;
  378.  TempIndex: Integer;
  379.  MidValue : Single;
  380. begin
  381.  Lo:= Left;
  382.  Hi:= Right;
  383.  MidValue:= PosArray[DepthOrder[(Left + Right) shr 1]].z;
  384.  repeat
  385.   while (PosArray[DepthOrder[Lo]].z < MidValue) do Inc(Lo);
  386.   while (MidValue < PosArray[DepthOrder[Hi]].z) do Dec(Hi);
  387.   if (Lo <= Hi) then
  388.    begin
  389.     TempIndex:= DepthOrder[Lo];
  390.     DepthOrder[Lo]:= DepthOrder[Hi];
  391.     DepthOrder[Hi]:= TempIndex;
  392.     Inc(Lo);
  393.     Dec(Hi);
  394.    end;
  395.  until (Lo > Hi);
  396.  if (Left < Hi) then SortDepthOrder(Left, Hi);
  397.  if (Lo < Right) then SortDepthOrder(Lo, Right);
  398. end;
  399. //---------------------------------------------------------------------------
  400. procedure TAsphyreBB.MakeDepthOrder();
  401. var
  402.  i: Integer;
  403. begin
  404.  SetLength(DepthOrder, PosArray.Count);
  405.  for i:= 0 to Length(DepthOrder) - 1 do
  406.   DepthOrder[i]:= i;
  407.  if (Length(DepthOrder) > 1) then
  408.   SortDepthOrder(0, Length(DepthOrder) - 1);
  409. end;
  410. //---------------------------------------------------------------------------
  411. procedure TAsphyreBB.AddToCache(const Pos: TVector4; const Points: TPoint4;
  412.  const Mapping: TPoint4; Color: Cardinal; Tex: TAsphyreCustomTexture;
  413.  DrawFx: Integer);
  414. var
  415.  Entry: PVertexRecord;
  416. begin
  417.  RequestCache(4, 6, DrawFx, Tex);
  418.  AddIndexEntry(FVertexCount + 0);
  419.  AddIndexEntry(FVertexCount + 1);
  420.  AddIndexEntry(FVertexCount + 2);
  421.  AddIndexEntry(FVertexCount + 3);
  422.  AddIndexEntry(FVertexCount + 0);
  423.  AddIndexEntry(FVertexCount + 2);
  424.  Entry:= NextVertexEntry();
  425.  Entry.Vertex  := Vector3(Points[3].x + Pos.x, Points[3].y + Pos.y, Pos.z);
  426.  Entry.Diffuse := Color;
  427.  Entry.TexCoord:= Mapping[3];
  428.  Inc(FVertexCount);
  429.  Entry:= NextVertexEntry();
  430.  Entry.Vertex  := Vector3(Points[0].x + Pos.x, Points[0].y + Pos.y, Pos.z);
  431.  Entry.Diffuse := Color;
  432.  Entry.TexCoord:= Mapping[0];
  433.  Inc(FVertexCount);
  434.  Entry:= NextVertexEntry();
  435.  Entry.Vertex  := Vector3(Points[1].x + Pos.x, Points[1].y + Pos.y, Pos.z);
  436.  Entry.Diffuse := Color;
  437.  Entry.TexCoord:= Mapping[1];
  438.  Inc(FVertexCount);
  439.  Entry:= NextVertexEntry();
  440.  Entry.Vertex  := Vector3(Points[2].x + Pos.x, Points[2].y + Pos.y, Pos.z);
  441.  Entry.Diffuse := Color;
  442.  Entry.TexCoord:= Mapping[2];
  443.  Inc(FVertexCount);
  444.  Inc(FPrimitives, 2);
  445. end;
  446. //---------------------------------------------------------------------------
  447. procedure TAsphyreBB.Render(ViewMtx, ProjMtx: PMatrix4);
  448. var
  449.  i, Index : Integer;
  450.  Billboard: PAsphyreBillboard;
  451.  Tex      : TAsphyreCustomTexture;
  452.  Mapping  : TPoint4;
  453.  Pos      : TVector4;
  454.  Points   : TPoint4;
  455. begin
  456.  PosArray.Transform(PosArray, ViewMtx);
  457.  MakeDepthOrder();
  458.  if (Length(DepthOrder) < 1) then Exit;
  459.  ResetDeviceStates();
  460.  with FDevice.Dev9 do
  461.   begin
  462.    SetRenderState(D3DRS_LIGHTING, iFalse);
  463.    SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
  464.    SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
  465.    SetRenderState(D3DRS_ALPHABLENDENABLE, iTrue);
  466.    SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
  467.    SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  468.    SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
  469.    SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
  470.    SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
  471.    SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
  472.    SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
  473.    SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
  474.    SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
  475.    SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
  476.    SetTransform(D3DTS_WORLD, TD3DXMatrix(IdentityMtx4));
  477.    SetTransform(D3DTS_VIEW, TD3DXMatrix(IdentityMtx4));
  478.    SetTransform(D3DTS_PROJECTION, PD3DXMatrix(ProjMtx)^);
  479.   end;
  480.  for i:= Length(DepthOrder) - 1 downto 0  do
  481.   begin
  482.    Index:= DepthOrder[i];
  483.    Billboard:= FBBList[Index];
  484.    Pos:= PosArray[Index];
  485.    if (Pos.z > 0.0) then
  486.     begin
  487.      if (Billboard.Image <> nil) then
  488.       begin
  489.        if (Billboard.Image is TAsphyreImage) then
  490.         begin
  491.          Tex:= TAsphyreImage(Billboard.Image).RetreiveTex(Billboard.Pattern,
  492.           @Mapping);
  493.         end else
  494.         begin
  495.          Tex:= Billboard.Image.Texture[0];
  496.          Mapping:= TexFull4;
  497.         end;
  498.       end else Tex:= nil;
  499.      Points:= pRotate4c(ZeroVec2, Billboard.Size, Billboard.Phi);
  500.      AddToCache(Pos, Points, Mapping, Billboard.Color, Tex, Billboard.DrawFx);
  501.     end;
  502.   end;
  503.  FBBList.Clear();
  504.  PosArray.RemoveAll();
  505.  ResetCache(); 
  506. end;
  507. //---------------------------------------------------------------------------
  508. end.