GXS.Imposter.pas 47 KB

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