GLS.Imposter.pas 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.Imposter;
  5. (* Imposter building and rendering implementation for GLScene *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. System.Types,
  12. System.Classes,
  13. System.SysUtils,
  14. System.Math,
  15. Stage.OpenGLTokens,
  16. GLS.Scene,
  17. GLS.Context,
  18. Stage.VectorTypes,
  19. Stage.VectorGeometry,
  20. GLS.PersistentClasses,
  21. Stage.PipelineTransform,
  22. GLS.Graphics,
  23. GLS.Color,
  24. GLS.RenderContextInfo,
  25. GLS.Coordinates,
  26. GLS.BaseClasses,
  27. GLS.State,
  28. Stage.TextureFormat,
  29. Stage.Utils;
  30. type
  31. (* Imposter rendering options.
  32. Following options are supported:
  33. impoBlended : the imposters are transparently blended during renders,
  34. this will smooth their edges but requires them to be rendered sorted
  35. from back to front
  36. impoAlphaTest : alpha test is used to eliminate transparent pixels,
  37. the alpha treshold is adjusted by the AlphaTreshold property
  38. impoNearestFiltering : use nearest texture filtering (the alternative
  39. is linear filtering)
  40. impoPerspectiveCorrection : activates a special imposter rendering
  41. projection suitable for distorting the sprites when seen from a level
  42. angle of view with a wide focal camera (think trees/grass when walking
  43. in a forest), if not active, the imposter sprites are camera-facing *)
  44. TImposterOption = (impoBlended, impoAlphaTest, impoNearestFiltering,
  45. impoPerspectiveCorrection);
  46. TImposterOptions = set of TImposterOption;
  47. const
  48. cDefaultImposterOptions = [impoBlended, impoAlphaTest];
  49. type
  50. TGLImposterBuilder = class;
  51. (* Base class for imposters manipulation and handling.
  52. Rendering imposters is performed by three methods, BeginRender must
  53. be invoked first, then Render for each of the impostr
  54. This class assumes a single impostor per texture.
  55. Note: Remeber to enable Destination Alpha on your viewer.*)
  56. TImposter = class(TObject)
  57. private
  58. FRequestCount: Integer;
  59. FBuilder: TGLImposterBuilder;
  60. FTexture: TGLTextureHandle;
  61. FImpostoredObject: TGLBaseSceneObject;
  62. FAspectRatio: Single;
  63. FModulated: Boolean;
  64. protected
  65. FVx, FVy: TGLVector;
  66. FStaticOffset: TGLVector;
  67. FQuad: array[0..3] of TGLVector;
  68. FStaticScale: Single;
  69. procedure PrepareTexture(var rci: TGLRenderContextInfo); virtual;
  70. procedure RenderQuad(const texExtents, objPos: TGLVector; size: Single);
  71. public
  72. constructor Create(aBuilder: TGLImposterBuilder); virtual;
  73. destructor Destroy; override;
  74. procedure BeginRender(var rci: TGLRenderContextInfo); virtual;
  75. procedure Render(var rci: TGLRenderContextInfo;
  76. const objPos, localCameraPos: TGLVector;
  77. size: Single); virtual;
  78. procedure EndRender(var rci: TGLRenderContextInfo); virtual;
  79. procedure RenderOnce(var rci: TGLRenderContextInfo;
  80. const objPos, localCameraPos: TGLVector;
  81. size: Single);
  82. property AspectRatio: Single read FAspectRatio write FAspectRatio;
  83. property Builder: TGLImposterBuilder read FBuilder;
  84. property Texture: TGLTextureHandle read FTexture;
  85. property ImpostoredObject: TGLBaseSceneObject read FImpostoredObject write
  86. FImpostoredObject;
  87. property Modulated: Boolean read FModulated write FModulated;
  88. end;
  89. // Imposter loading events
  90. TLoadingImposterEvent = function (Sender : TObject; impostoredObject :
  91. TGLBaseSceneObject; destImposter : TImposter) : TGLBitmap32 of object;
  92. {$NODEFINE TLoadingImposterEvent}
  93. //Used CPPB procedure instead of Delphi function
  94. //TLoadingImposterEvent = procedure (Sender : TObject; impostoredObject : TGLBaseSceneObject; destImposter : TImposter; var result : TGLBitmap32) of object;
  95. {$HPPEMIT 'typedef GLS.Graphics::TGLBitmap32* __fastcall (__closure *TLoadingImposterEvent)(System::TObject* Sender, Glscene::TGLBaseSceneObject* impostoredObject, TImposter* destImposter);'}
  96. TImposterLoadedEvent = procedure (Sender : TObject; impostoredObject : TGLBaseSceneObject;
  97. destImposter : TImposter) of object;
  98. TImposterReference = (irCenter, irTop, irBottom);
  99. // Abstract ImposterBuilder class.
  100. TGLImposterBuilder = class(TGLUpdateAbleComponent)
  101. private
  102. FBackColor: TGLColor;
  103. FBuildOffset: TGLCoordinates;
  104. FImposterRegister: TGLPersistentObjectList;
  105. FRenderPoint: TGLRenderPoint;
  106. FImposterOptions: TImposterOptions;
  107. FAlphaTreshold: Single;
  108. FImposterReference: TImposterReference;
  109. FOnLoadingImposter: TLoadingImposterEvent;
  110. FOnImposterLoaded: TImposterLoadedEvent;
  111. protected
  112. procedure SetRenderPoint(AValue: TGLRenderPoint);
  113. procedure RenderPointFreed(Sender: TObject);
  114. procedure SetBackColor(AValue: TGLColor);
  115. procedure SetBuildOffset(AValue: TGLCoordinates);
  116. procedure SetImposterReference(AValue: TImposterReference);
  117. procedure InitializeImpostorTexture(const TextureSize: TPoint);
  118. property ImposterRegister: TGLPersistentObjectList read FImposterRegister;
  119. procedure UnregisterImposter(imposter: TImposter);
  120. function CreateNewImposter: TImposter; virtual;
  121. procedure PrepareImposters(Sender: TObject; var rci: TGLRenderContextInfo);
  122. virtual;
  123. procedure DoPrepareImposter(var rci: TGLRenderContextInfo;
  124. impostoredObject: TGLBaseSceneObject;
  125. destImposter: TImposter); virtual; abstract;
  126. procedure DoUserSpecifiedImposter(
  127. var rci: TGLRenderContextInfo;
  128. destImposter: TImposter;
  129. bmp32: TGLBitmap32); virtual;
  130. public
  131. constructor Create(AOwner: TComponent); override;
  132. destructor Destroy; override;
  133. procedure Notification(AComponent: TComponent; Operation: TOperation);
  134. override;
  135. procedure NotifyChange(Sender: TObject); override;
  136. (* Returns a valid imposter for the specified object.
  137. Imposter must have been requested first, and the builder given
  138. an opportunity to prepare it before it can be available. *)
  139. function ImposterFor(impostoredObject: TGLBaseSceneObject): TImposter;
  140. // Request an imposter to be prepared for the specified object.
  141. procedure RequestImposterFor(impostoredObject: TGLBaseSceneObject);
  142. // Tells the imposter for the specified object is no longer needed.
  143. procedure UnRequestImposterFor(impostoredObject: TGLBaseSceneObject);
  144. published
  145. (* Specifies the render point at which the impostor texture(s) can be prepared.
  146. For best result, the render point should happen in viewer that has
  147. a destination alpha (otherwise, impostors will be opaque). *)
  148. property RenderPoint: TGLRenderPoint read FRenderPoint write SetRenderPoint;
  149. (* Background color for impostor rendering.
  150. Typically, you'll want to leave the alpha channel to zero, and pick
  151. as RGB as color that matches the impostor'ed objects edge colors most.*)
  152. property BackColor: TGLColor read FBackColor write SetBackColor;
  153. (* Offset applied to the impostor'ed object during imposter construction.
  154. Can be used to manually tune the centering of objects. *)
  155. property BuildOffset: TGLCoordinates read FBuildOffset write SetBuildOffset;
  156. // Imposter rendering options.
  157. property ImposterOptions: TImposterOptions read FImposterOptions write
  158. FImposterOptions default cDefaultImposterOptions;
  159. (* Determines how the imposter are handled.
  160. This is the reference point for imposters, impostor'ed objects that
  161. are centered should use irCenter, those whose bottom is the origin
  162. should use irBottom, etc. *)
  163. property ImposterReference: TImposterReference read FImposterReference write
  164. SetImposterReference default irCenter;
  165. // Alpha testing teshold.
  166. property AlphaTreshold: Single read FAlphaTreshold write FAlphaTreshold;
  167. (* Event fired before preparing/loading an imposter.
  168. If an already prepared version of the importer is available, place
  169. it in the TGLBitmap32 the event shall return (the bitmap will be
  170. freed by the imposter builder). If a bitmap is specified, it will
  171. be used in place of what automatic generation could have generated. *)
  172. property OnLoadingImposter: TLoadingImposterEvent read FOnLoadingImposter
  173. write FOnLoadingImposter;
  174. (* Event fired after preparing/loading an imposter.
  175. This events gives an opportunity to save the imposter after it has
  176. been loaded or prepared. *)
  177. property OnImposterLoaded: TImposterLoadedEvent read FOnImposterLoaded write
  178. FOnImposterLoaded;
  179. end;
  180. // Describes a set of orientation in a corona fashion.
  181. TGLStaticImposterBuilderCorona = class(TCollectionItem)
  182. private
  183. FSamples: Integer;
  184. FElevation: Single;
  185. FSampleBaseIndex: Integer;
  186. protected
  187. function GetDisplayName: string; override;
  188. procedure SetSamples(AValue: Integer);
  189. procedure SetElevation(AValue: Single);
  190. public
  191. constructor Create(ACollection: TCollection); override;
  192. destructor Destroy; override;
  193. procedure Assign(Source: TPersistent); override;
  194. published
  195. property Samples: Integer read FSamples write SetSamples default 8;
  196. property Elevation: Single read FElevation write SetElevation;
  197. end;
  198. TCoronaTangentLookup = record
  199. minTan, maxTan: Single;
  200. corona: TGLStaticImposterBuilderCorona;
  201. end;
  202. TGLStaticImposterBuilderCoronas = class(TOwnedCollection)
  203. private
  204. FCoronaTangentLookup: array of TCoronaTangentLookup;
  205. protected
  206. procedure SetItems(AIndex: Integer; const AValue:
  207. TGLStaticImposterBuilderCorona);
  208. function GetItems(AIndex: Integer): TGLStaticImposterBuilderCorona;
  209. procedure Update(Item: TCollectionItem); override;
  210. procedure PrepareSampleBaseIndices;
  211. procedure PrepareCoronaTangentLookup;
  212. function CoronaForElevationTangent(aTangent: Single):
  213. TGLStaticImposterBuilderCorona;
  214. public
  215. constructor Create(AOwner: TPersistent);
  216. function Add: TGLStaticImposterBuilderCorona; overload;
  217. function Add(const elevation: Single; samples: Integer):
  218. TGLStaticImposterBuilderCorona; overload;
  219. property Items[AIndex: Integer]: TGLStaticImposterBuilderCorona read GetItems
  220. write SetItems; default;
  221. function SampleCount: Integer;
  222. procedure NotifyChange; virtual;
  223. procedure EndUpdate; override;
  224. end;
  225. // Imposter class whose texture contains several views from different angles.
  226. TStaticImposter = class(TImposter)
  227. public
  228. procedure Render(var rci: TGLRenderContextInfo;
  229. const objPos, localCameraPos: TGLVector;
  230. size: Single); override;
  231. end;
  232. TSIBLigthing = (siblNoLighting, siblStaticLighting, siblLocalLighting);
  233. // Builds imposters whose texture is a catalog of prerendered views.
  234. TGLStaticImposterBuilder = class(TGLImposterBuilder)
  235. private
  236. FCoronas: TGLStaticImposterBuilderCoronas;
  237. FSampleSize: Integer;
  238. FTextureSize: TPoint;
  239. FSamplesPerAxis: TPoint;
  240. FInvSamplesPerAxis: TVector2f;
  241. FSamplingRatioBias, FInvSamplingRatioBias: Single;
  242. FLighting: TSIBLigthing;
  243. FSamplesAlphaScale: Single;
  244. protected
  245. procedure SetCoronas(AValue: TGLStaticImposterBuilderCoronas);
  246. procedure SetSampleSize(AValue: Integer);
  247. procedure SetSamplingRatioBias(AValue: Single);
  248. function StoreSamplingRatioBias: Boolean;
  249. procedure SetLighting(AValue: TSIBLigthing);
  250. procedure SetSamplesAlphaScale(AValue: Single);
  251. function StoreSamplesAlphaScale: Boolean;
  252. function GetTextureSizeInfo: string;
  253. procedure SetTextureSizeInfo(const texSize: string);
  254. // Computes the optimal texture size that would be able to hold all samples.
  255. function ComputeOptimalTextureSize: TPoint;
  256. function CreateNewImposter: TImposter; override;
  257. procedure DoPrepareImposter(var rci: TGLRenderContextInfo;
  258. impostoredObject: TGLBaseSceneObject;
  259. destImposter: TImposter); override;
  260. procedure DoUserSpecifiedImposter(
  261. var rci: TGLRenderContextInfo;
  262. destImposter: TImposter;
  263. bmp32: TGLBitmap32); override;
  264. procedure ComputeStaticParams(destImposter: TImposter);
  265. public
  266. constructor Create(AOwner: TComponent); override;
  267. destructor Destroy; override;
  268. (* Render imposter texture.
  269. Buffer and object must be compatible, RC must have been activated. *)
  270. procedure Render(var rci: TGLRenderContextInfo;
  271. impostoredObject: TGLBaseSceneObject;
  272. destImposter: TImposter);
  273. (* Ratio (0..1) of the texture that will be used by samples.
  274. If this value is below 1, you're wasting texture space and may
  275. as well increase the number of samples. *)
  276. function TextureFillRatio: Single;
  277. // Meaningful only after imposter texture has been prepared.
  278. property TextureSize: TPoint read FTextureSize;
  279. property SamplesPerAxis: TPoint read FSamplesPerAxis;
  280. published
  281. // Description of the samples looking orientations.
  282. property Coronas: TGLStaticImposterBuilderCoronas read FCoronas write
  283. SetCoronas;
  284. // Size of the imposter samples (square).
  285. property SampleSize: Integer read FSampleSize write SetSampleSize default 32;
  286. (* Size ratio applied to the impostor'ed objects during sampling.
  287. Values greater than one can be used to "fill" the samples more
  288. by scaling up the object. This is especially useful when the impostor'ed
  289. object doesn't fill its bounding sphere, and/or if the outer details
  290. are not relevant for impostoring. *)
  291. property SamplingRatioBias: Single read FSamplingRatioBias write
  292. SetSamplingRatioBias stored StoreSamplingRatioBias;
  293. (* Scale factor apply to the sample alpha channel.
  294. Main use is to saturate the samples alpha channel, and make fully
  295. opaque what would have been partially transparent, while leaving
  296. fully transparent what was fully transparent. *)
  297. property SamplesAlphaScale: Single read FSamplesAlphaScale write
  298. SetSamplesAlphaScale stored StoreSamplesAlphaScale;
  299. // Lighting mode to apply during samples construction.
  300. property Lighting: TSIBLigthing read FLighting write FLighting default
  301. siblStaticLighting;
  302. (* Dummy property that returns the size of the imposter texture.
  303. This property is essentially here as a helper at design time,
  304. to give you the requirements your coronas and samplesize parameters
  305. imply. *)
  306. property TextureSizeInfo: string read GetTextureSizeInfo write
  307. SetTextureSizeInfo stored False;
  308. end;
  309. TGLDynamicImposterBuilder = class(TGLImposterBuilder)
  310. private
  311. FMinTexSize, FMaxTexSize: Integer;
  312. FMinDistance, FTolerance: Single;
  313. FUseMatrixError: Boolean;
  314. protected
  315. procedure SetMinDistance(const AValue: Single);
  316. public
  317. constructor Create(AOwner: TComponent); override;
  318. destructor Destroy; override;
  319. /// procedure DoRender(var rci : TGLRenderContextInfo; renderSelf, renderChildren : Boolean); override;
  320. published
  321. property MinTexSize: Integer read FMinTexSize write FMinTexSize;
  322. property MaxTexSize: Integer read FMaxTexSize write FMaxTexSize;
  323. property MinDistance: Single read FMinDistance write SetMinDistance;
  324. property Tolerance: Single read FTolerance write FTolerance;
  325. property UseMatrixError: Boolean read FUseMatrixError write FUseMatrixError;
  326. end;
  327. TGLImposter = class(TGLImmaterialSceneObject)
  328. private
  329. FBuilder: TGLImposterBuilder;
  330. FImpostoredObject: TGLBaseSceneObject;
  331. protected
  332. procedure SetBuilder(const AValue: TGLImposterBuilder);
  333. procedure SetImpostoredObject(const AValue: TGLBaseSceneObject);
  334. public
  335. constructor Create(AOwner: TComponent); override;
  336. destructor Destroy; override;
  337. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  338. procedure DoRender(var ARci: TGLRenderContextInfo;
  339. ARenderSelf, ARenderChildren: Boolean); override;
  340. published
  341. property Builder: TGLImposterBuilder read FBuilder write SetBuilder;
  342. property ImpostoredObject: TGLBaseSceneObject read FImpostoredObject write
  343. SetImpostoredObject;
  344. end;
  345. //-------------------------------------------------------------
  346. implementation
  347. //-------------------------------------------------------------
  348. const
  349. cReferenceToPos: array[Low(TImposterReference)..High(TImposterReference)] of Single = (0, -1, 1);
  350. // ----------
  351. // ---------- TImposter ----------
  352. // ----------
  353. constructor TImposter.Create(aBuilder: TGLImposterBuilder);
  354. begin
  355. inherited Create;
  356. FBuilder := aBuilder;
  357. FTexture := TGLTextureHandle.Create;
  358. aBuilder.FImposterRegister.Add(Self);
  359. FAspectRatio := 1;
  360. end;
  361. destructor TImposter.Destroy;
  362. begin
  363. if Assigned(FBuilder) then
  364. FBuilder.UnregisterImposter(Self);
  365. FTexture.Free;
  366. inherited;
  367. end;
  368. procedure TImposter.PrepareTexture(var rci: TGLRenderContextInfo);
  369. var
  370. i: Integer;
  371. begin
  372. if FTexture.Handle <> 0 then
  373. Exit;
  374. FTexture.AllocateHandle;
  375. FTexture.Target := ttTexture2D;
  376. rci.GLStates.TextureBinding[0, ttTexture2D] := FTexture.Handle;
  377. if gl.EXT_texture_edge_clamp then
  378. i := GL_CLAMP_TO_EDGE
  379. else
  380. i := GL_CLAMP;
  381. begin
  382. gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, i);
  383. gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, i);
  384. gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  385. gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  386. end;
  387. end;
  388. procedure TImposter.BeginRender(var rci: TGLRenderContextInfo);
  389. var
  390. mat: TGLMatrix;
  391. filter: Cardinal;
  392. fx, fy, yOffset, cosAlpha, dynScale: Single;
  393. begin
  394. with rci.GLStates do
  395. begin
  396. Disable(stLighting);
  397. Disable(stCullFace);
  398. ActiveTextureEnabled[ttTexture2D] := True;
  399. if impoAlphaTest in Builder.ImposterOptions then
  400. begin
  401. Enable(stAlphaTest);
  402. SetGLAlphaFunction(cfGEqual, Builder.AlphaTreshold);
  403. end
  404. else
  405. Disable(stAlphaTest);
  406. if impoBlended in Builder.ImposterOptions then
  407. begin
  408. Enable(stBlend);
  409. SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  410. end
  411. else
  412. Disable(stBlend);
  413. TextureBinding[0, ttTexture2D] := Texture.Handle;
  414. if impoNearestFiltering in Builder.ImposterOptions then
  415. filter := GL_NEAREST
  416. else
  417. filter := GL_LINEAR;
  418. gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
  419. gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
  420. if FModulated then
  421. begin
  422. gl.Color4fv(@XYZWHmgVector);
  423. gl.TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  424. end
  425. else
  426. gl.TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  427. mat := rci.PipelineTransformation.ModelViewMatrix^;
  428. FVx.X := mat.X.X;
  429. FVx.Y := mat.Y.X;
  430. FVx.Z := mat.Z.X;
  431. NormalizeVector(FVx);
  432. FVy.X := mat.X.Y;
  433. FVy.Y := mat.Y.Y;
  434. FVy.Z := mat.Z.Y;
  435. NormalizeVector(FVy);
  436. if impoPerspectiveCorrection in Builder.ImposterOptions then
  437. begin
  438. cosAlpha := VectorDotProduct(FVy, YHmgVector);
  439. FVy := VectorLerp(FVy, YHmgVector, Abs(cosAlpha));
  440. NormalizeVector(FVy);
  441. dynScale := ClampValue(1 / cosAlpha, 1, 1.414) * FStaticScale;
  442. end
  443. else
  444. dynScale := FStaticScale;
  445. fx := Sqrt(FAspectRatio);
  446. fy := 1 / fx;
  447. yOffset := cReferenceToPos[Builder.ImposterReference] * dynScale * fy;
  448. fx := fx * dynScale;
  449. fy := fy * dynScale;
  450. FQuad[0] := VectorSubtract(VectorCombine(FVx, FVy, fx, fy + yOffset),
  451. FStaticOffset);
  452. FQuad[1] := VectorSubtract(VectorCombine(FVx, FVy, -fx, fy + yOffset),
  453. FStaticOffset);
  454. FQuad[2] := VectorSubtract(VectorCombine(FVx, FVy, -fx, -fy + yOffset),
  455. FStaticOffset);
  456. FQuad[3] := VectorSubtract(VectorCombine(FVx, FVy, fx, -fy + yOffset),
  457. FStaticOffset);
  458. gl.Begin_(GL_QUADS);
  459. end;
  460. end;
  461. procedure TImposter.Render(var rci: TGLRenderContextInfo;
  462. const objPos, localCameraPos: TGLVector;
  463. size: Single);
  464. const
  465. cQuadTexExtents: TGLVector = (X:0; Y:0; Z:1; W:1);
  466. begin
  467. RenderQuad(cQuadTexExtents, objPos, size);
  468. end;
  469. procedure TImposter.RenderQuad(const texExtents, objPos: TGLVector; size: Single);
  470. var
  471. pos: TGLVector;
  472. begin
  473. VectorCombine(objPos, FQuad[0], size, pos);
  474. gl.TexCoord2f(texExtents.Z, texExtents.W);
  475. gl.Vertex3fv(@pos);
  476. VectorCombine(objPos, FQuad[1], size, pos);
  477. gl.TexCoord2f(texExtents.X, texExtents.W);
  478. gl.Vertex3fv(@pos);
  479. VectorCombine(objPos, FQuad[2], size, pos);
  480. gl.TexCoord2f(texExtents.X, texExtents.Y);
  481. gl.Vertex3fv(@pos);
  482. VectorCombine(objPos, FQuad[3], size, pos);
  483. gl.TexCoord2f(texExtents.Z, texExtents.Y);
  484. gl.Vertex3fv(@pos);
  485. end;
  486. procedure TImposter.EndRender(var rci: TGLRenderContextInfo);
  487. begin
  488. gl.End_;
  489. rci.GLStates.ActiveTextureEnabled[ttTexture2D] := False;
  490. end;
  491. procedure TImposter.RenderOnce(var rci: TGLRenderContextInfo;
  492. const objPos, localCameraPos: TGLVector;
  493. size: Single);
  494. begin
  495. BeginRender(rci);
  496. Render(rci, objPos, localCameraPos, size);
  497. EndRender(rci);
  498. end;
  499. // ----------
  500. // ---------- TGLImposterBuilder ----------
  501. // ----------
  502. constructor TGLImposterBuilder.Create(AOwner: TComponent);
  503. begin
  504. inherited;
  505. FImposterRegister := TGLPersistentObjectList.Create;
  506. FBackColor := TGLColor.CreateInitialized(Self, clrTransparent);
  507. FBuildOffset := TGLCoordinates.CreateInitialized(Self, NullHmgPoint, CsPoint);
  508. FImposterOptions := cDefaultImposterOptions;
  509. FAlphaTreshold := 0.5;
  510. end;
  511. destructor TGLImposterBuilder.Destroy;
  512. var
  513. i: Integer;
  514. begin
  515. FBuildOffset.Free;
  516. FBackColor.Free;
  517. for i := 0 to FImposterRegister.Count - 1 do
  518. TImposter(FImposterRegister[i]).FBuilder := nil;
  519. FImposterRegister.CleanFree;
  520. inherited;
  521. end;
  522. procedure TGLImposterBuilder.Notification(AComponent: TComponent; Operation:
  523. TOperation);
  524. var
  525. i: Integer;
  526. imposter: TImposter;
  527. begin
  528. if Operation = opRemove then
  529. begin
  530. if AComponent = FRenderPoint then
  531. FRenderPoint := nil;
  532. for i := FImposterRegister.Count - 1 downto 0 do
  533. begin
  534. imposter := TImposter(FImposterRegister[i]);
  535. if imposter.ImpostoredObject = AComponent then
  536. begin
  537. imposter.Free;
  538. Break;
  539. end;
  540. end;
  541. end;
  542. inherited;
  543. end;
  544. function TGLImposterBuilder.CreateNewImposter: TImposter;
  545. begin
  546. Result := TImposter.Create(Self);
  547. end;
  548. procedure TGLImposterBuilder.PrepareImposters(Sender: TObject; var rci:
  549. TGLRenderContextInfo);
  550. var
  551. i: Integer;
  552. imp: TImposter;
  553. bmp32: TGLBitmap32;
  554. begin
  555. for i := 0 to ImposterRegister.Count - 1 do
  556. begin
  557. imp := TImposter(ImposterRegister[i]);
  558. if (imp.ImpostoredObject <> nil) and (imp.Texture.Handle = 0) then
  559. begin
  560. if Assigned(FOnLoadingImposter) then
  561. bmp32:=FOnLoadingImposter(Self, imp.ImpostoredObject, imp)
  562. else
  563. bmp32 := nil;
  564. if not Assigned(bmp32) then
  565. DoPrepareImposter(rci, imp.ImpostoredObject, imp)
  566. else
  567. begin
  568. DoUserSpecifiedImposter(rci, imp, bmp32);
  569. bmp32.Free;
  570. end;
  571. if Assigned(FOnImposterLoaded) then
  572. FOnImposterLoaded(Self, imp.ImpostoredObject, imp);
  573. end;
  574. end;
  575. end;
  576. procedure TGLImposterBuilder.DoUserSpecifiedImposter(
  577. var rci: TGLRenderContextInfo;
  578. destImposter: TImposter;
  579. bmp32: TGLBitmap32);
  580. var
  581. size: Integer;
  582. begin
  583. destImposter.PrepareTexture(rci);
  584. bmp32.RegisterAsOpenGLTexture(
  585. destImposter.FTexture, False, GL_RGBA8, size, size, size);
  586. end;
  587. procedure TGLImposterBuilder.NotifyChange(Sender: TObject);
  588. var
  589. i: Integer;
  590. begin
  591. for i := 0 to FImposterRegister.Count - 1 do
  592. TImposter(FImposterRegister[i]).Texture.DestroyHandle;
  593. inherited;
  594. end;
  595. function TGLImposterBuilder.ImposterFor(impostoredObject: TGLBaseSceneObject):
  596. TImposter;
  597. var
  598. i: Integer;
  599. begin
  600. for i := 0 to FImposterRegister.Count - 1 do
  601. begin
  602. Result := TImposter(FImposterRegister[i]);
  603. if Result.ImpostoredObject = impostoredObject then
  604. Exit;
  605. end;
  606. Result := nil;
  607. end;
  608. procedure TGLImposterBuilder.RequestImposterFor(impostoredObject:
  609. TGLBaseSceneObject);
  610. var
  611. imposter: TImposter;
  612. begin
  613. if impostoredObject = nil then
  614. Exit;
  615. imposter := ImposterFor(impostoredObject);
  616. if imposter = nil then
  617. begin
  618. imposter := CreateNewImposter;
  619. imposter.ImpostoredObject := impostoredObject;
  620. end;
  621. Inc(imposter.FRequestCount);
  622. end;
  623. procedure TGLImposterBuilder.UnRequestImposterFor(impostoredObject:
  624. TGLBaseSceneObject);
  625. var
  626. imposter: TImposter;
  627. begin
  628. if impostoredObject = nil then
  629. Exit;
  630. imposter := ImposterFor(impostoredObject);
  631. if imposter <> nil then
  632. begin
  633. Dec(imposter.FRequestCount);
  634. if imposter.FRequestCount = 0 then
  635. imposter.Free;
  636. end;
  637. end;
  638. procedure TGLImposterBuilder.SetRenderPoint(AValue: TGLRenderPoint);
  639. begin
  640. if AValue <> FRenderPoint then
  641. begin
  642. if Assigned(FRenderPoint) then
  643. begin
  644. FRenderPoint.RemoveFreeNotification(Self);
  645. FRenderPoint.UnRegisterCallBack(PrepareImposters);
  646. end;
  647. FRenderPoint := AValue;
  648. if Assigned(FRenderPoint) then
  649. begin
  650. FRenderPoint.FreeNotification(Self);
  651. FRenderPoint.RegisterCallBack(PrepareImposters, RenderPointFreed);
  652. end;
  653. end;
  654. end;
  655. procedure TGLImposterBuilder.RenderPointFreed(Sender: TObject);
  656. begin
  657. FRenderPoint := nil;
  658. end;
  659. procedure TGLImposterBuilder.SetBackColor(AValue: TGLColor);
  660. begin
  661. FBackColor.Assign(AValue);
  662. end;
  663. procedure TGLImposterBuilder.SetBuildOffset(AValue: TGLCoordinates);
  664. begin
  665. FBuildOffset.Assign(AValue);
  666. end;
  667. procedure TGLImposterBuilder.SetImposterReference(AValue: TImposterReference);
  668. begin
  669. if FImposterReference <> AValue then
  670. begin
  671. FImposterReference := AValue;
  672. NotifyChange(Self);
  673. end;
  674. end;
  675. procedure TGLImposterBuilder.InitializeImpostorTexture(const textureSize:
  676. TPoint);
  677. begin
  678. gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureSize.X, textureSize.Y, 0,
  679. GL_RGBA, GL_UNSIGNED_BYTE, nil);
  680. end;
  681. procedure TGLImposterBuilder.UnregisterImposter(imposter: TImposter);
  682. begin
  683. if imposter.Builder = Self then
  684. begin
  685. FImposterRegister.Remove(imposter);
  686. imposter.FBuilder := nil;
  687. end;
  688. end;
  689. // ----------
  690. // ---------- TGLStaticImposterBuilderCorona ----------
  691. // ----------
  692. constructor TGLStaticImposterBuilderCorona.Create(ACollection: TCollection);
  693. begin
  694. inherited;
  695. FSamples := 8;
  696. end;
  697. destructor TGLStaticImposterBuilderCorona.Destroy;
  698. begin
  699. inherited;
  700. end;
  701. procedure TGLStaticImposterBuilderCorona.Assign(Source: TPersistent);
  702. begin
  703. if Source is TGLStaticImposterBuilderCorona then
  704. begin
  705. FSamples := TGLStaticImposterBuilderCorona(Source).FSamples;
  706. FElevation := TGLStaticImposterBuilderCorona(Source).FElevation;
  707. end;
  708. inherited;
  709. end;
  710. function TGLStaticImposterBuilderCorona.GetDisplayName: string;
  711. begin
  712. Result := Format('%.1f° / %d samples', [Elevation, Samples]);
  713. end;
  714. procedure TGLStaticImposterBuilderCorona.SetSamples(AValue: Integer);
  715. begin
  716. if AValue <> FSamples then
  717. begin
  718. FSamples := AValue;
  719. if FSamples < 1 then
  720. FSamples := 1;
  721. (Collection as TGLStaticImposterBuilderCoronas).NotifyChange;
  722. end;
  723. end;
  724. procedure TGLStaticImposterBuilderCorona.SetElevation(AValue: Single);
  725. begin
  726. if AValue <> FElevation then
  727. begin
  728. FElevation := ClampValue(AValue, -89, 89);
  729. (Collection as TGLStaticImposterBuilderCoronas).NotifyChange;
  730. end;
  731. end;
  732. // ----------
  733. // ---------- TGLStaticImposterBuilderCoronas ----------
  734. // ----------
  735. constructor TGLStaticImposterBuilderCoronas.Create(AOwner: TPersistent);
  736. begin
  737. inherited Create(AOwner, TGLStaticImposterBuilderCorona);
  738. end;
  739. function TGLStaticImposterBuilderCoronas.Add: TGLStaticImposterBuilderCorona;
  740. begin
  741. Result := (inherited Add) as TGLStaticImposterBuilderCorona;
  742. end;
  743. function TGLStaticImposterBuilderCoronas.Add(const elevation: Single;
  744. samples: Integer): TGLStaticImposterBuilderCorona;
  745. begin
  746. Result := (inherited Add) as TGLStaticImposterBuilderCorona;
  747. Result.Elevation := elevation;
  748. Result.Samples := samples;
  749. end;
  750. procedure TGLStaticImposterBuilderCoronas.SetItems(AIndex: Integer; const
  751. AValue: TGLStaticImposterBuilderCorona);
  752. begin
  753. inherited Items[AIndex] := AValue;
  754. end;
  755. function TGLStaticImposterBuilderCoronas.GetItems(AIndex: Integer):
  756. TGLStaticImposterBuilderCorona;
  757. begin
  758. Result := TGLStaticImposterBuilderCorona(inherited Items[AIndex]);
  759. end;
  760. procedure TGLStaticImposterBuilderCoronas.Update(Item: TCollectionItem);
  761. begin
  762. inherited;
  763. NotifyChange;
  764. end;
  765. procedure TGLStaticImposterBuilderCoronas.NotifyChange;
  766. begin
  767. if (UpdateCount = 0) and (GetOwner <> nil) and (GetOwner is
  768. TGLUpdateAbleComponent) then
  769. TGLUpdateAbleComponent(GetOwner).NotifyChange(Self);
  770. end;
  771. procedure TGLStaticImposterBuilderCoronas.EndUpdate;
  772. begin
  773. inherited;
  774. NotifyChange;
  775. end;
  776. function TGLStaticImposterBuilderCoronas.SampleCount: Integer;
  777. var
  778. i: Integer;
  779. begin
  780. Result := 0;
  781. for i := 0 to Count - 1 do
  782. Result := Result + Items[i].Samples;
  783. end;
  784. procedure TGLStaticImposterBuilderCoronas.PrepareSampleBaseIndices;
  785. var
  786. p, i: Integer;
  787. begin
  788. p := 0;
  789. for i := 0 to Count - 1 do
  790. begin
  791. Items[i].FSampleBaseIndex := p;
  792. Inc(p, Items[i].Samples);
  793. end;
  794. end;
  795. procedure TGLStaticImposterBuilderCoronas.PrepareCoronaTangentLookup;
  796. var
  797. i, j: Integer;
  798. corona: TGLStaticImposterBuilderCorona;
  799. boundary: Single;
  800. begin
  801. SetLength(FCoronaTangentLookup, Count);
  802. // place them in the array and sort by ascending elevation
  803. for i := 0 to Count - 1 do
  804. FCoronaTangentLookup[i].corona := Items[i];
  805. for i := 0 to Count - 2 do
  806. for j := i + 1 to Count - 1 do
  807. if FCoronaTangentLookup[j].corona.Elevation <
  808. FCoronaTangentLookup[i].corona.Elevation then
  809. begin
  810. corona := FCoronaTangentLookup[j].corona;
  811. FCoronaTangentLookup[j].corona := FCoronaTangentLookup[i].corona;
  812. FCoronaTangentLookup[i].corona := corona;
  813. end;
  814. // adjust min max then intermediate boundaries
  815. FCoronaTangentLookup[0].minTan := -1e30;
  816. FCoronaTangentLookup[Count - 1].minTan := 1e30;
  817. for i := 0 to Count - 2 do
  818. begin
  819. boundary := Tan((0.5 * cPIdiv180) * (FCoronaTangentLookup[i].corona.Elevation
  820. + FCoronaTangentLookup[i + 1].corona.Elevation));
  821. FCoronaTangentLookup[i].maxTan := boundary;
  822. FCoronaTangentLookup[i + 1].minTan := boundary;
  823. end;
  824. end;
  825. function TGLStaticImposterBuilderCoronas.CoronaForElevationTangent(aTangent:
  826. Single): TGLStaticImposterBuilderCorona;
  827. var
  828. i, n: Integer;
  829. begin
  830. n := High(FCoronaTangentLookup);
  831. if (n = 0) or (aTangent <= FCoronaTangentLookup[0].maxTan) then
  832. Result := FCoronaTangentLookup[0].corona
  833. else if aTangent > FCoronaTangentLookup[n].minTan then
  834. Result := FCoronaTangentLookup[n].corona
  835. else
  836. begin
  837. Result := FCoronaTangentLookup[1].corona;
  838. for i := 2 to n - 2 do
  839. begin
  840. if aTangent <= FCoronaTangentLookup[i].minTan then
  841. Break;
  842. Result := FCoronaTangentLookup[i].corona;
  843. end;
  844. end;
  845. end;
  846. // ----------
  847. // ---------- TStaticImposter ----------
  848. // ----------
  849. procedure TStaticImposter.Render(var rci: TGLRenderContextInfo;
  850. const objPos, localCameraPos: TGLVector;
  851. size: Single);
  852. var
  853. azimuthAngle: Single;
  854. i: Integer;
  855. x, y: Word;
  856. bestCorona: TGLStaticImposterBuilderCorona;
  857. texExtents: TGLVector;
  858. tdx, tdy: Single;
  859. siBuilder: TGLStaticImposterBuilder;
  860. begin // inherited; exit;
  861. siBuilder := TGLStaticImposterBuilder(Builder);
  862. // determine closest corona
  863. bestCorona := siBuilder.Coronas.CoronaForElevationTangent(
  864. localCameraPos.Y / VectorLength(localCameraPos.X, localCameraPos.Z));
  865. // determine closest sample in corona
  866. azimuthAngle := FastArcTangent2(localCameraPos.Z, localCameraPos.X) + cPI;
  867. i := Round(azimuthAngle * bestCorona.Samples * cInv2PI);
  868. if i < 0 then
  869. i := 0
  870. else if i >= bestCorona.Samples then
  871. i := bestCorona.Samples - 1;
  872. i := bestCorona.FSampleBaseIndex + i;
  873. tdx := siBuilder.FInvSamplesPerAxis.X;
  874. tdy := siBuilder.FInvSamplesPerAxis.Y;
  875. DivMod(i, siBuilder.SamplesPerAxis.X, y, x);
  876. texExtents.X := tdx * x;
  877. texExtents.Y := tdy * y;
  878. texExtents.Z := texExtents.X + tdx;
  879. texExtents.W := texExtents.Y + tdy;
  880. // then render it
  881. RenderQuad(texExtents, objPos, Size);
  882. end;
  883. // ----------
  884. // ---------- TGLStaticImposterBuilder ----------
  885. // ----------
  886. constructor TGLStaticImposterBuilder.Create(AOwner: TComponent);
  887. begin
  888. inherited;
  889. FCoronas := TGLStaticImposterBuilderCoronas.Create(Self);
  890. FCoronas.Add;
  891. FSampleSize := 16;
  892. FSamplingRatioBias := 1;
  893. FInvSamplingRatioBias := 1;
  894. FLighting := siblStaticLighting;
  895. FSamplesAlphaScale := 1;
  896. end;
  897. destructor TGLStaticImposterBuilder.Destroy;
  898. begin
  899. FCoronas.Free;
  900. inherited;
  901. end;
  902. function TGLStaticImposterBuilder.CreateNewImposter: TImposter;
  903. begin
  904. Result := TStaticImposter.Create(Self);
  905. end;
  906. procedure TGLStaticImposterBuilder.SetCoronas(AValue:
  907. TGLStaticImposterBuilderCoronas);
  908. begin
  909. FCoronas.Assign(AValue);
  910. NotifyChange(Self);
  911. end;
  912. procedure TGLStaticImposterBuilder.SetSampleSize(AValue: Integer);
  913. begin
  914. AValue := RoundUpToPowerOf2(AValue);
  915. if AValue < 8 then
  916. AValue := 8;
  917. if AValue > 1024 then
  918. AValue := 1024;
  919. if AValue <> FSampleSize then
  920. begin
  921. FSampleSize := AValue;
  922. NotifyChange(Self);
  923. end;
  924. end;
  925. procedure TGLStaticImposterBuilder.SetSamplingRatioBias(AValue: Single);
  926. begin
  927. AValue := ClampValue(AValue, 0.1, 10);
  928. if AValue <> FSamplingRatioBias then
  929. begin
  930. FSamplingRatioBias := AValue;
  931. FInvSamplingRatioBias := 1 / AValue;
  932. NotifyChange(Self);
  933. end;
  934. end;
  935. function TGLStaticImposterBuilder.StoreSamplingRatioBias: Boolean;
  936. begin
  937. Result := (FSamplingRatioBias <> 1);
  938. end;
  939. procedure TGLStaticImposterBuilder.SetLighting(AValue: TSIBLigthing);
  940. begin
  941. if AValue <> FLighting then
  942. begin
  943. FLighting := AValue;
  944. NotifyChange(Self);
  945. end;
  946. end;
  947. procedure TGLStaticImposterBuilder.SetSamplesAlphaScale(AValue: Single);
  948. begin
  949. if FSamplesAlphaScale <> AValue then
  950. begin
  951. FSamplesAlphaScale := AValue;
  952. NotifyChange(Self);
  953. end;
  954. end;
  955. function TGLStaticImposterBuilder.StoreSamplesAlphaScale: Boolean;
  956. begin
  957. Result := (FSamplesAlphaScale <> 1);
  958. end;
  959. function TGLStaticImposterBuilder.GetTextureSizeInfo: string;
  960. var
  961. t: TPoint;
  962. fill: Integer;
  963. begin
  964. t := ComputeOptimalTextureSize;
  965. Result := Format('%d x %d', [t.X, t.Y]);
  966. fill := Coronas.SampleCount * SampleSize * SampleSize;
  967. if fill < t.X * t.Y then
  968. Result := Result + Format(' (%.1f%%)', [(100 * fill) / (t.X * t.Y)]);
  969. end;
  970. procedure TGLStaticImposterBuilder.SetTextureSizeInfo(const texSize: string);
  971. begin
  972. // do nothing, this is a dummy property!
  973. end;
  974. procedure TGLStaticImposterBuilder.DoPrepareImposter(var rci:
  975. TGLRenderContextInfo;
  976. impostoredObject: TGLBaseSceneObject; destImposter: TImposter);
  977. begin
  978. Render(rci, impostoredObject, destImposter);
  979. end;
  980. procedure TGLStaticImposterBuilder.DoUserSpecifiedImposter(
  981. var rci: TGLRenderContextInfo;
  982. destImposter:
  983. TImposter;
  984. bmp32: TGLBitmap32);
  985. begin
  986. inherited;
  987. FTextureSize.X := bmp32.Width;
  988. FTextureSize.Y := bmp32.Height;
  989. ComputeStaticParams(destImposter);
  990. end;
  991. procedure TGLStaticImposterBuilder.ComputeStaticParams(destImposter: TImposter);
  992. var
  993. radius: Single;
  994. begin
  995. Coronas.PrepareCoronaTangentLookup;
  996. Coronas.PrepareSampleBaseIndices;
  997. FSamplesPerAxis.X := FTextureSize.X div SampleSize;
  998. FSamplesPerAxis.Y := FTextureSize.Y div SampleSize;
  999. FInvSamplesPerAxis.X := 1 / FSamplesPerAxis.X;
  1000. FInvSamplesPerAxis.Y := 1 / FSamplesPerAxis.Y;
  1001. Assert(FSamplesPerAxis.X * FSamplesPerAxis.Y >= Coronas.SampleCount,
  1002. 'User specified bitmap and imposter parameters don''t match');
  1003. radius := destImposter.ImpostoredObject.BoundingSphereRadius /
  1004. SamplingRatioBias;
  1005. if ImposterReference = irCenter then
  1006. destImposter.FStaticScale := radius
  1007. else
  1008. destImposter.FStaticScale := radius * 0.5;
  1009. destImposter.FStaticOffset := FBuildOffset.DirectVector;
  1010. end;
  1011. procedure TGLStaticImposterBuilder.Render(var rci: TGLRenderContextInfo;
  1012. impostoredObject: TGLBaseSceneObject; destImposter: TImposter);
  1013. var
  1014. i, coronaIdx, curSample: Integer;
  1015. radius: Single;
  1016. cameraDirection, cameraOffset: TGLVector;
  1017. xDest, xSrc, yDest, ySrc: Integer;
  1018. corona: TGLStaticImposterBuilderCorona;
  1019. fx, fy, yOffset: Single;
  1020. LM: TGLMatrix;
  1021. begin
  1022. FTextureSize := ComputeOptimalTextureSize;
  1023. if (FTextureSize.X <= 0) and (FTextureSize.Y <= 0) then
  1024. begin
  1025. SampleSize := SampleSize shr 1;
  1026. Assert(False,
  1027. 'Too many samples, can''t fit in a texture! Reduce SampleSize.');
  1028. end;
  1029. ComputeStaticParams(destImposter);
  1030. radius := impostoredObject.BoundingSphereRadius / SamplingRatioBias;
  1031. if ImposterReference <> irCenter then
  1032. radius := radius * 0.5;
  1033. Assert((rci.GLStates.ViewPort.Z >= SampleSize) and (rci.GLStates.ViewPort.W >= SampleSize),
  1034. 'ViewPort too small to render imposter samples!');
  1035. // Setup the buffer in a suitable fashion for our needs
  1036. with FBackColor do
  1037. rci.GLStates.ColorClearValue := Color;
  1038. if Lighting = siblNoLighting then
  1039. rci.GLStates.Disable(stLighting);
  1040. rci.PipelineTransformation.Push;
  1041. fx := radius * rci.GLStates.ViewPort.Z / SampleSize;
  1042. fy := radius * rci.GLStates.ViewPort.W / SampleSize;
  1043. yOffset := cReferenceToPos[ImposterReference] * radius;
  1044. rci.PipelineTransformation.SetProjectionMatrix(
  1045. CreateOrthoMatrix(-fx, fx, yOffset - fy, yOffset + fy, radius * 0.5, radius * 5));
  1046. xSrc := (rci.GLStates.ViewPort.Z - SampleSize) div 2;
  1047. ySrc := (rci.GLStates.ViewPort.W - SampleSize) div 2;
  1048. // setup imposter texture
  1049. if destImposter.Texture.Handle = 0 then
  1050. begin
  1051. {$IFDEF USE_OPENGL_DEBUG}
  1052. if gl.GREMEDY_string_marker then
  1053. gl.StringMarkerGREMEDY(22, 'Imposter texture setup');
  1054. {$ENDIF}
  1055. destImposter.PrepareTexture(rci);
  1056. InitializeImpostorTexture(FTextureSize);
  1057. end;
  1058. gl.PixelTransferf(GL_ALPHA_SCALE, FSamplesAlphaScale);
  1059. // Now render each sample
  1060. curSample := 0;
  1061. for coronaIdx := 0 to Coronas.Count - 1 do
  1062. begin
  1063. corona := Coronas[coronaIdx];
  1064. cameraDirection := XHmgVector;
  1065. RotateVector(cameraDirection, ZHmgPoint, corona.Elevation * cPIdiv180);
  1066. for i := 0 to corona.Samples - 1 do
  1067. begin
  1068. cameraOffset := cameraDirection;
  1069. RotateVector(cameraOffset, YHmgVector, (c2PI * i) / corona.Samples);
  1070. ScaleVector(cameraOffset, -radius * 2);
  1071. rci.GLStates.DepthWriteMask := True;
  1072. gl.Clear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
  1073. LM := CreateLookAtMatrix(cameraOffset, NullHmgVector, YHmgVector);
  1074. if Lighting = siblStaticLighting then
  1075. (rci.scene as TGLScene).SetupLights(rci.GLStates.MaxLights);
  1076. rci.PipelineTransformation.SetViewMatrix(MatrixMultiply(
  1077. CreateTranslationMatrix(FBuildOffset.AsVector), LM));
  1078. impostoredObject.Render(rci);
  1079. gl.CheckError;
  1080. xDest := (curSample mod FSamplesPerAxis.X) * SampleSize;
  1081. yDest := (curSample div FSamplesPerAxis.X) * SampleSize;
  1082. rci.GLStates.TextureBinding[0, ttTexture2D] :=
  1083. destImposter.Texture.Handle;
  1084. gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, xDest, yDest, xSrc, ySrc,
  1085. SampleSize, SampleSize);
  1086. Inc(curSample);
  1087. end;
  1088. end;
  1089. // Restore buffer stuff
  1090. gl.PixelTransferf(GL_ALPHA_SCALE, 1);
  1091. rci.PipelineTransformation.Pop;
  1092. gl.Clear(GL_COLOR_BUFFER_BIT + GL_DEPTH_BUFFER_BIT);
  1093. if Lighting = siblStaticLighting then
  1094. (rci.scene as TGLScene).SetupLights(rci.GLStates.MaxLights);
  1095. end;
  1096. function TGLStaticImposterBuilder.ComputeOptimalTextureSize: TPoint;
  1097. var
  1098. nbSamples, maxSamples, maxTexSize, baseSize: Integer;
  1099. texDim, bestTexDim: TPoint;
  1100. requiredSurface, currentSurface, bestSurface: Integer;
  1101. begin
  1102. nbSamples := Coronas.SampleCount;
  1103. if CurrentGLContext = nil then
  1104. maxTexSize := 16 * 1024
  1105. else
  1106. gl.GetIntegerv(GL_MAX_TEXTURE_SIZE, @maxTexSize);
  1107. maxSamples := Sqr(maxTexSize div SampleSize);
  1108. if nbSamples < maxSamples then
  1109. begin
  1110. Result.X := -1;
  1111. Result.Y := -1;
  1112. end;
  1113. requiredSurface := nbSamples * SampleSize * SampleSize;
  1114. baseSize := RoundUpToPowerOf2(SampleSize);
  1115. // determine the texture size with the best fill ratio
  1116. bestSurface := MaxInt;
  1117. texDim.X := baseSize;
  1118. while texDim.X <= maxTexSize do
  1119. begin
  1120. texDim.Y := baseSize;
  1121. while texDim.Y <= maxTexSize do
  1122. begin
  1123. currentSurface := texDim.X * texDim.Y;
  1124. if currentSurface >= requiredSurface then
  1125. begin
  1126. if currentSurface < bestSurface then
  1127. begin
  1128. bestTexDim := texDim;
  1129. bestSurface := currentSurface;
  1130. end
  1131. else if (currentSurface = bestSurface)
  1132. and (MaxInteger(texDim.X, texDim.Y) < MaxInteger(bestTexDim.X,
  1133. bestTexDim.Y)) then
  1134. begin
  1135. bestTexDim := texDim;
  1136. bestSurface := currentSurface;
  1137. end
  1138. else
  1139. Break;
  1140. end;
  1141. texDim.Y := texDim.Y * 2;
  1142. end;
  1143. texDim.X := texDim.X * 2;
  1144. end;
  1145. Assert(bestSurface <> MaxInt);
  1146. Result := bestTexDim;
  1147. end;
  1148. function TGLStaticImposterBuilder.TextureFillRatio: Single;
  1149. var
  1150. texDim: TPoint;
  1151. begin
  1152. texDim := ComputeOptimalTextureSize;
  1153. Result := (Coronas.SampleCount * SampleSize * SampleSize) / (texDim.X *
  1154. texDim.Y);
  1155. end;
  1156. // ----------
  1157. // ---------- TGLDynamicImposterBuilder ----------
  1158. // ----------
  1159. constructor TGLDynamicImposterBuilder.Create(AOwner: TComponent);
  1160. begin
  1161. inherited;
  1162. FTolerance := 0.1;
  1163. FUseMatrixError := True;
  1164. FMinTexSize := 16;
  1165. FMaxTexSize := 64;
  1166. end;
  1167. destructor TGLDynamicImposterBuilder.Destroy;
  1168. begin
  1169. inherited;
  1170. end;
  1171. {
  1172. procedure TGLDynamicImposterBuilder.DoRender(var rci : TGLRenderContextInfo;
  1173. renderSelf, renderChildren : Boolean);
  1174. var
  1175. i, size, Left, Top, Width, Height : Integer;
  1176. imposter : TGLImposter;
  1177. mat, projection, modelview : TGLMatrix;
  1178. BackColor, pos, temp : TGLVector;
  1179. rad : Single;
  1180. AABB : TAABB;
  1181. begin
  1182. if (csDesigning in ComponentState) or not FEnabled then exit;
  1183. // Store the current clear color
  1184. glGetFloatv(GL_COLOR_CLEAR_VALUE, @BackColor[0]);
  1185. // Get the projection matrix
  1186. if UseMatrixError then
  1187. glGetFloatv(GL_PROJECTION_MATRIX, @projection);
  1188. // Render and save each imposter as required
  1189. for i:=0 to FImposterRegister.Count-1 do begin
  1190. imposter:=TGLImposter(FImposterRegister[i]);
  1191. if (imposter.Count = 0) or not imposter.Visible then Continue;
  1192. imposter.FDrawImposter:=True;
  1193. if VectorDistance(imposter.AbsolutePosition, rci.cameraPosition)<FMinDistance then begin
  1194. imposter.FDrawImposter:=False;
  1195. Continue;
  1196. end;
  1197. glMatrixMode(GL_MODELVIEW);
  1198. glPushMatrix;
  1199. glMultMatrixf(@imposter.AbsoluteMatrixAsAddress[0]);
  1200. glGetFloatv(GL_MODELVIEW_MATRIX, @modelview);
  1201. // Get imposters dimensions
  1202. AABB:=imposter.AxisAlignedBoundingBox;
  1203. rad:=MaxFloat(AABB.max[0],AABB.max[1],AABB.max[2]);
  1204. pos:=imposter.AbsolutePosition;
  1205. temp:=Scene.CurrentBuffer.Camera.AbsoluteEyeSpaceVector(0,1,0);
  1206. temp:=VectorAdd(pos, VectorScale(temp,rad));
  1207. pos:=Scene.CurrentBuffer.WorldToScreen(pos);
  1208. temp:=Scene.CurrentBuffer.WorldToScreen(temp);
  1209. size:=RoundUpToPowerOf2(Round(2*VectorDistance(pos,temp)));
  1210. if size<FMinTexSize then size:=FMinTexSize;
  1211. if size>FMaxTexSize then begin
  1212. imposter.FDrawImposter:=False;
  1213. glPopMatrix;
  1214. Continue;
  1215. end;
  1216. temp:=pos;
  1217. temp[0]:=temp[0]+size;
  1218. temp:=Scene.CurrentBuffer.ScreenToWorld(temp);
  1219. Imposter.FSize:=VectorDistance(imposter.AbsolutePosition,temp);
  1220. imposter.FTexSize:=size;
  1221. pos[0]:=pos[0]-size/2;
  1222. pos[1]:=pos[1]-size/2;
  1223. // Calculate error
  1224. if UseMatrixError then begin
  1225. mat:=MatrixMultiply(modelview, projection);
  1226. if (imposter.CalcError(mat)>FTolerance) or (imposter.FInvalidated) then
  1227. imposter.FOldMatrix:=mat
  1228. else begin
  1229. glPopMatrix;
  1230. Continue;
  1231. end;
  1232. end;
  1233. // Clear to transparent black
  1234. glClearColor(0,0,0,0);
  1235. // Determine size by color (for debug purposes)
  1236. (*case size of
  1237. 16 : glClearColor(0,0,1,0.1);
  1238. 32 : glClearColor(0,1,0,0.1);
  1239. 64 : glClearColor(1,0,0,0.1);
  1240. 128 : glClearColor(1,1,0,0.1);
  1241. 256 : glClearColor(1,0,1,0.1);
  1242. end;// *)
  1243. glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
  1244. // Render the imposter's children
  1245. imposter.RenderChildren(0, imposter.Count-1, rci);
  1246. glPopMatrix;
  1247. // Select the imposters texture (will create the handle if null)
  1248. glBindTexture(GL_TEXTURE_2D,imposter.TextureHandle);
  1249. // Check for resize or invalidation
  1250. if (imposter.FTexSize <> imposter.FLastTexSize)
  1251. or (imposter.FInvalidated) then begin
  1252. glTexImage2d(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
  1253. imposter.FLastTexSize:=imposter.FTexSize;
  1254. imposter.FInvalidated:=False;
  1255. imposter.NotifyChange(self);
  1256. end;
  1257. // Get the region to be copied from the frame buffer
  1258. Left:=Floor(pos[0]); Top:=Floor(pos[1]);
  1259. Width:=Size; Height:=Size;
  1260. // ... Perhaps some region clamping here?
  1261. // Copy the frame buffer pixels to the imposter texture
  1262. glCopyTexSubImage2d(GL_TEXTURE_2D, 0, 0, 0,
  1263. Left, Top, Width, Height);
  1264. end;
  1265. // Reset the clear color and clear color, depth and stencil buffers
  1266. glClearColor(BackColor[0],BackColor[1],BackColor[2],BackColor[3]);
  1267. glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
  1268. end;
  1269. }
  1270. procedure TGLDynamicImposterBuilder.SetMinDistance(const AValue: Single);
  1271. begin
  1272. if AValue <> FMinDistance then
  1273. begin
  1274. FMinDistance := AValue;
  1275. NotifyChange(Self);
  1276. end;
  1277. end;
  1278. // ----------
  1279. // ---------- TGLImposter ----------
  1280. // ----------
  1281. constructor TGLImposter.Create(AOwner: TComponent);
  1282. begin
  1283. inherited;
  1284. ObjectStyle := ObjectStyle + [osDirectDraw];
  1285. end;
  1286. destructor TGLImposter.Destroy;
  1287. begin
  1288. Builder := nil;
  1289. ImpostoredObject := nil;
  1290. inherited;
  1291. end;
  1292. procedure TGLImposter.Notification(AComponent: TComponent; Operation:
  1293. TOperation);
  1294. begin
  1295. if Operation = opRemove then
  1296. begin
  1297. if AComponent = Builder then
  1298. Builder := nil;
  1299. if AComponent = ImpostoredObject then
  1300. ImpostoredObject := nil;
  1301. end;
  1302. inherited;
  1303. end;
  1304. procedure TGLImposter.DoRender(var ARci: TGLRenderContextInfo;
  1305. ARenderSelf, ARenderChildren: Boolean);
  1306. var
  1307. camPos: TGLVector;
  1308. imposter: TImposter;
  1309. begin
  1310. if ARenderSelf and Assigned(Builder) and Assigned(ImpostoredObject) then
  1311. begin
  1312. imposter := Builder.ImposterFor(ImpostoredObject);
  1313. if Assigned(imposter) and (imposter.Texture.Handle <> 0) then
  1314. begin
  1315. camPos := AbsoluteToLocal(ARci.cameraPosition);
  1316. imposter.BeginRender(ARci);
  1317. imposter.Render(ARci, NullHmgPoint, camPos, Scale.MaxXYZ);
  1318. imposter.EndRender(ARci);
  1319. end;
  1320. end;
  1321. if ARenderChildren then
  1322. Self.RenderChildren(0, Count - 1, ARci);
  1323. end;
  1324. procedure TGLImposter.SetBuilder(const AValue: TGLImposterBuilder);
  1325. begin
  1326. if AValue <> FBuilder then
  1327. begin
  1328. if Assigned(FBuilder) then
  1329. begin
  1330. FBuilder.RemoveFreeNotification(Self);
  1331. FBuilder.UnRequestImposterFor(ImpostoredObject);
  1332. end;
  1333. FBuilder := AValue;
  1334. if Assigned(FBuilder) then
  1335. begin
  1336. FBuilder.FreeNotification(Self);
  1337. FBuilder.RequestImposterFor(ImpostoredObject);
  1338. end;
  1339. end;
  1340. end;
  1341. procedure TGLImposter.SetImpostoredObject(const AValue: TGLBaseSceneObject);
  1342. begin
  1343. if AValue <> FImpostoredObject then
  1344. begin
  1345. if Assigned(Builder) then
  1346. FBuilder.UnRequestImposterFor(ImpostoredObject);
  1347. FImpostoredObject := AValue;
  1348. if Assigned(Builder) then
  1349. FBuilder.RequestImposterFor(ImpostoredObject);
  1350. end;
  1351. end;
  1352. {
  1353. function TGLImposter.AxisAlignedDimensionsUnscaled : TGLVector;
  1354. begin
  1355. Result:=NullHMGVector;
  1356. end;
  1357. function TGLImposter.CalcError(NewMatrix : TGLMatrix) : Single;
  1358. var
  1359. i : Integer;
  1360. mat : TGLMatrix;
  1361. err : Single;
  1362. begin
  1363. err:=0;
  1364. mat:=NewMatrix;
  1365. InvertMatrix(mat);
  1366. mat:=MatrixMultiply(FOldMatrix, mat);
  1367. for i:=0 to 3 do mat[i][i]:=mat[i][i]-1;
  1368. for i:=0 to 15 do err:=err+Abs(mat[i div 4][i mod 4]);
  1369. Result:=err;
  1370. end;
  1371. function TGLImposter.GetTextureHandle: Cardinal;
  1372. begin
  1373. if FTextureHandle = 0 then
  1374. glGenTextures(1, @FTextureHandle);
  1375. Result:=FTextureHandle;
  1376. end;
  1377. procedure TGLImposter.Invalidate;
  1378. begin
  1379. FInvalidated:=True;
  1380. end;
  1381. }
  1382. //--------------------------------------------
  1383. initialization
  1384. //--------------------------------------------
  1385. // RegisterClasses([TGLDynamicImposterBuilder, TGLImposter]);
  1386. RegisterClasses([TGLImposter]);
  1387. end.