GXS.MaterialMultiProxy.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.MaterialMultiProxy;
  5. (*
  6. Implements a multi-proxy object, useful for discreet LOD.
  7. Allows assign a unique material for each proxy master.
  8. What changed compared to GLMultiProxy:
  9. 1) Allows assign a unique material for each proxy master
  10. 2) TgxMaterialMultiProxyMaster: FDistanceMin, FDistanceMax removed
  11. 3) TgxMaterialMultiProxy = class(TgxBaseSceneObject)!!!
  12. 4) TgxMaterialMultiProxyMaster.Visible removed
  13. 5) TgxMaterialMultiProxy.MaterialLibrary added
  14. 6) TgxMaterialMultiProxyMaster.MasterLibMaterial added
  15. 7) TgxMaterialMultiProxyMasters.Add overloaded
  16. 8) Implemented a new mechanizm of connecting TgxLibMaterial and TgxLibMaterialName
  17. (they are connected on assigning, not while rendering; full persistency support;
  18. allows to assign directly to TgxLibMaterial)
  19. 9) FMX-style code formating
  20. *)
  21. interface
  22. {$I Stage.Defines.inc}
  23. uses
  24. Winapi.OpenGL,
  25. System.Classes,
  26. System.SysUtils,
  27. GXS.BaseClasses,
  28. GXS.PersistentClasses,
  29. Stage.VectorTypes,
  30. Stage.VectorGeometry,
  31. Stage.Strings,
  32. GXS.Texture,
  33. GXS.Material,
  34. GXS.Silhouette,
  35. GXS.Scene,
  36. GXS.RenderContextInfo,
  37. GXS.Context,
  38. Stage.PipelineTransform;
  39. type
  40. TgxMaterialMultiProxy = class;
  41. // MasterObject description for a MultiProxy object.
  42. TgxMaterialMultiProxyMaster = class(TgxInterfacedCollectionItem, IgxMaterialLibrarySupported)
  43. private
  44. FMasterObject: TgxBaseSceneObject;
  45. FMasterLibMaterial: TgxLibMaterial;
  46. FTempLibMaterialName: TgxLibMaterialName;
  47. FDistanceMin2, FDistanceMax2: Single;
  48. procedure SetMasterLibMaterialName(const Value: TgxLibMaterialName);
  49. function GetMasterLibMaterialName: TgxLibMaterialName;
  50. // Implementing IGLMaterialLibrarySupported.
  51. function GetMaterialLibrary: TgxAbstractMaterialLibrary;
  52. protected
  53. function GetDisplayName: string; override;
  54. procedure SetMasterObject(const Val: TgxBaseSceneObject);
  55. procedure SetDistanceMin(const Val: Single);
  56. procedure SetDistanceMax(const Val: Single);
  57. function GetDistanceMin: Single;
  58. function GetDistanceMax: Single;
  59. public
  60. constructor Create(Collection: TCollection); override;
  61. destructor Destroy; override;
  62. procedure Assign(Source: TPersistent); override;
  63. function OwnerObject: TgxMaterialMultiProxy;
  64. procedure NotifyChange;
  65. { Specifies the Material, that current master object will use.
  66. Provides a faster way to access FMasterLibMaterial, compared to
  67. MasterLibMaterialName }
  68. property MasterLibMaterial: TgxLibMaterial read FMasterLibMaterial write FMasterLibMaterial stored False;
  69. published
  70. { Specifies the Master object which will be proxy'ed. }
  71. property MasterObject: TgxBaseSceneObject read FMasterObject write SetMasterObject;
  72. { Specifies the Material, that current master object will use. }
  73. property MasterLibMaterialName: TgxLibMaterialName read GetMasterLibMaterialName write SetMasterLibMaterialName;
  74. { Minimum visibility Distance (inclusive). }
  75. property DistanceMin: Single read GetDistanceMin write SetDistanceMin;
  76. { Maximum visibility Distance (exclusive). }
  77. property DistanceMax: Single read GetDistanceMax write SetDistanceMax;
  78. end;
  79. { Collection of TgxMaterialMultiProxyMaster. }
  80. TgxMaterialMultiProxyMasters = class(TOwnedCollection)
  81. protected
  82. procedure SetItems(index: Integer; const Val: TgxMaterialMultiProxyMaster);
  83. function GetItems(index: Integer): TgxMaterialMultiProxyMaster;
  84. procedure Update(Item: TCollectionItem); override;
  85. procedure Notification(AComponent: TComponent); virtual;
  86. public
  87. constructor Create(AOwner: TPersistent);
  88. function Add: TgxMaterialMultiProxyMaster; overload;
  89. function Add(Master: TgxBaseSceneObject; DistanceMin, DistanceMax: Single): TgxMaterialMultiProxyMaster; overload;
  90. function Add(Master: TgxBaseSceneObject; MasterLibMaterial: TgxLibMaterial; DistanceMin, DistanceMax: Single): TgxMaterialMultiProxyMaster; overload;
  91. property Items[index: Integer]: TgxMaterialMultiProxyMaster read GetItems write SetItems; default;
  92. procedure NotifyChange;
  93. procedure EndUpdate; override;
  94. end;
  95. { Multiple Proxy object.
  96. This proxy has multiple Master objects, which are individually made visible
  97. depending on a Distance to the camera criterion. It can be used to implement
  98. discreet level of detail directly for static objects, or objects that
  99. go through cyclic animation.
  100. For dimensionsn raycasting and silhouette purposes, the first Master is used
  101. (item zero in the MasterObjects collection). }
  102. TgxMaterialMultiProxy = class(TgxBaseSceneObject)
  103. private
  104. FMasterObjects: TgxMaterialMultiProxyMasters;
  105. FRendering: Boolean; // internal use (loop protection)
  106. FMaterialLibrary: TgxMaterialLibrary;
  107. procedure SetMaterialLibrary(const Value: TgxMaterialLibrary);
  108. protected
  109. procedure SetMasterObjects(const Val: TgxMaterialMultiProxyMasters);
  110. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  111. function PrimaryMaster: TgxBaseSceneObject;
  112. public
  113. constructor Create(AOwner: TComponent); override;
  114. destructor Destroy; override;
  115. procedure Assign(Source: TPersistent); override;
  116. procedure DoRender(var rci: TgxRenderContextInfo; renderSelf, renderChildren: Boolean); override;
  117. function AxisAlignedDimensionsUnscaled: TVector4f; override;
  118. function RayCastIntersect(const rayStart, rayVector: TVector4f; intersectPoint: PVector4f = nil; intersectNormal: PVector4f = nil): Boolean; override;
  119. function GenerateSilhouette(const silhouetteParameters: TgxSilhouetteParameters): TgxSilhouette; override;
  120. published
  121. property MasterObjects: TgxMaterialMultiProxyMasters read FMasterObjects write SetMasterObjects;
  122. property MaterialLibrary: TgxMaterialLibrary read FMaterialLibrary write SetMaterialLibrary;
  123. property ObjectsSorting;
  124. property Direction;
  125. property PitchAngle;
  126. property Position;
  127. property RollAngle;
  128. property Scale;
  129. property ShowAxes;
  130. property TurnAngle;
  131. property Up;
  132. property Visible;
  133. property OnProgress;
  134. property Behaviours;
  135. property Effects;
  136. end;
  137. //-------------------------------------------------------------
  138. implementation
  139. //-------------------------------------------------------------
  140. // ------------------
  141. // ------------------ TgxMaterialMultiProxyMaster ------------------
  142. // ------------------
  143. constructor TgxMaterialMultiProxyMaster.Create(Collection: TCollection);
  144. begin
  145. inherited Create(Collection);
  146. end;
  147. destructor TgxMaterialMultiProxyMaster.Destroy;
  148. begin
  149. MasterObject := nil;
  150. inherited Destroy;
  151. end;
  152. procedure TgxMaterialMultiProxyMaster.Assign(Source: TPersistent);
  153. begin
  154. if Source is TgxMaterialMultiProxyMaster then
  155. begin
  156. FMasterObject := TgxMaterialMultiProxyMaster(Source).FMasterObject;
  157. FTempLibMaterialName := TgxMaterialMultiProxyMaster(Source).FTempLibMaterialName;
  158. FDistanceMin2 := TgxMaterialMultiProxyMaster(Source).FDistanceMin2;
  159. FDistanceMax2 := TgxMaterialMultiProxyMaster(Source).FDistanceMax2;
  160. NotifyChange;
  161. end
  162. else
  163. inherited;
  164. end;
  165. function TgxMaterialMultiProxyMaster.OwnerObject: TgxMaterialMultiProxy;
  166. begin
  167. if Collection = nil then
  168. Result := nil
  169. else
  170. Result := TgxMaterialMultiProxy(TgxMaterialMultiProxyMasters(Collection).GetOwner);
  171. end;
  172. procedure TgxMaterialMultiProxyMaster.NotifyChange;
  173. begin
  174. TgxMaterialMultiProxyMasters(Collection).NotifyChange;
  175. end;
  176. function TgxMaterialMultiProxyMaster.GetDisplayName: string;
  177. begin
  178. if MasterObject <> nil then
  179. Result := MasterObject.Name
  180. else
  181. Result := '???';
  182. Result := Result + Format(' [%.2f; %.2f[', [DistanceMin, DistanceMax]);
  183. end;
  184. procedure TgxMaterialMultiProxyMaster.SetMasterObject(const Val: TgxBaseSceneObject);
  185. begin
  186. if FMasterObject <> Val then
  187. begin
  188. if Assigned(FMasterObject) then
  189. FMasterObject.RemoveFreeNotification(OwnerObject);
  190. FMasterObject := Val;
  191. if Assigned(FMasterObject) then
  192. FMasterObject.FreeNotification(OwnerObject);
  193. NotifyChange;
  194. end;
  195. end;
  196. procedure TgxMaterialMultiProxyMaster.SetDistanceMin(const Val: Single);
  197. var
  198. tmp: Single;
  199. begin
  200. tmp := Sqr(Val);
  201. if FDistanceMin2 <> tmp then
  202. begin
  203. FDistanceMin2 := tmp;
  204. NotifyChange;
  205. end;
  206. end;
  207. procedure TgxMaterialMultiProxyMaster.SetDistanceMax(const Val: Single);
  208. var
  209. tmp: Single;
  210. begin
  211. tmp := Sqr(Val);
  212. if FDistanceMax2 <> tmp then
  213. begin
  214. FDistanceMax2 := tmp;
  215. NotifyChange;
  216. end;
  217. end;
  218. function TgxMaterialMultiProxyMaster.GetMaterialLibrary: TgxAbstractMaterialLibrary;
  219. begin
  220. if OwnerObject = nil then
  221. Result := nil
  222. else
  223. Result := OwnerObject.FMaterialLibrary;
  224. end;
  225. function TgxMaterialMultiProxyMaster.GetDistanceMax: Single;
  226. begin
  227. Result := sqrt(FDistanceMax2);
  228. end;
  229. function TgxMaterialMultiProxyMaster.GetDistanceMin: Single;
  230. begin
  231. Result := sqrt(FDistanceMin2);
  232. end;
  233. procedure TgxMaterialMultiProxyMaster.SetMasterLibMaterialName(
  234. const Value: TgxLibMaterialName);
  235. begin
  236. if OwnerObject.FMaterialLibrary = nil then
  237. begin
  238. FTempLibMaterialName := Value;
  239. if not (csLoading in OwnerObject.ComponentState) then
  240. raise ETexture.Create(strErrorEx + strMatLibNotDefined);
  241. end
  242. else
  243. begin
  244. FMasterLibMaterial := OwnerObject.FMaterialLibrary.LibMaterialByName(Value);
  245. FTempLibMaterialName := '';
  246. end;
  247. end;
  248. function TgxMaterialMultiProxyMaster.GetMasterLibMaterialName: TgxLibMaterialName;
  249. begin
  250. Result := OwnerObject.FMaterialLibrary.GetNameOfLibMaterial(FMasterLibMaterial);
  251. if Result = '' then
  252. Result := FTempLibMaterialName;
  253. end;
  254. // ------------------
  255. // ------------------ TgxMaterialMultiProxyMasters ------------------
  256. // ------------------
  257. constructor TgxMaterialMultiProxyMasters.Create(AOwner: TPersistent);
  258. begin
  259. inherited Create(AOwner, TgxMaterialMultiProxyMaster);
  260. end;
  261. procedure TgxMaterialMultiProxyMasters.SetItems(index: Integer;
  262. const Val: TgxMaterialMultiProxyMaster);
  263. begin
  264. inherited Items[index] := Val;
  265. end;
  266. function TgxMaterialMultiProxyMasters.GetItems(index: Integer): TgxMaterialMultiProxyMaster;
  267. begin
  268. Result := TgxMaterialMultiProxyMaster(inherited Items[index]);
  269. end;
  270. procedure TgxMaterialMultiProxyMasters.Update(Item: TCollectionItem);
  271. begin
  272. inherited;
  273. NotifyChange;
  274. end;
  275. function TgxMaterialMultiProxyMasters.Add: TgxMaterialMultiProxyMaster;
  276. begin
  277. Result := (inherited Add) as TgxMaterialMultiProxyMaster;
  278. end;
  279. function TgxMaterialMultiProxyMasters.Add(Master: TgxBaseSceneObject;
  280. DistanceMin, DistanceMax: Single): TgxMaterialMultiProxyMaster;
  281. begin
  282. BeginUpdate;
  283. Result := (inherited Add) as TgxMaterialMultiProxyMaster;
  284. Result.MasterObject := Master;
  285. Result.DistanceMin := DistanceMin;
  286. Result.DistanceMax := DistanceMax;
  287. EndUpdate;
  288. end;
  289. procedure TgxMaterialMultiProxyMasters.Notification(AComponent: TComponent);
  290. var
  291. I: Integer;
  292. begin
  293. for I := 0 to Count - 1 do
  294. with Items[I] do
  295. if FMasterObject = AComponent then
  296. FMasterObject := nil;
  297. end;
  298. procedure TgxMaterialMultiProxyMasters.NotifyChange;
  299. begin
  300. if (UpdateCount = 0) and (GetOwner <> nil) and (GetOwner is TgxUpdateAbleComponent) then
  301. TgxUpdateAbleComponent(GetOwner).NotifyChange(Self);
  302. end;
  303. procedure TgxMaterialMultiProxyMasters.EndUpdate;
  304. begin
  305. inherited EndUpdate;
  306. // Workaround for a bug in VCL's EndUpdate
  307. if UpdateCount = 0 then
  308. NotifyChange;
  309. end;
  310. function TgxMaterialMultiProxyMasters.Add(Master: TgxBaseSceneObject;
  311. MasterLibMaterial: TgxLibMaterial;
  312. DistanceMin, DistanceMax: Single): TgxMaterialMultiProxyMaster;
  313. begin
  314. BeginUpdate;
  315. Result := (inherited Add) as TgxMaterialMultiProxyMaster;
  316. Result.MasterObject := Master;
  317. Result.FMasterLibMaterial := MasterLibMaterial;
  318. Result.DistanceMin := DistanceMin;
  319. Result.DistanceMax := DistanceMax;
  320. EndUpdate;
  321. end;
  322. // ------------------
  323. // ------------------ TgxMaterialMultiProxy ------------------
  324. // ------------------
  325. constructor TgxMaterialMultiProxy.Create(AOwner: TComponent);
  326. begin
  327. inherited Create(AOwner);
  328. ObjectStyle := ObjectStyle + [osDirectDraw];
  329. FMasterObjects := TgxMaterialMultiProxyMasters.Create(Self);
  330. end;
  331. destructor TgxMaterialMultiProxy.Destroy;
  332. begin
  333. inherited Destroy;
  334. FMasterObjects.Free;
  335. end;
  336. procedure TgxMaterialMultiProxy.Notification(AComponent: TComponent; Operation: TOperation);
  337. begin
  338. if Operation = opRemove then
  339. begin
  340. FMasterObjects.Notification(AComponent);
  341. end;
  342. inherited;
  343. end;
  344. procedure TgxMaterialMultiProxy.SetMasterObjects(const Val: TgxMaterialMultiProxyMasters);
  345. begin
  346. FMasterObjects.Assign(Val);
  347. StructureChanged;
  348. end;
  349. procedure TgxMaterialMultiProxy.Assign(Source: TPersistent);
  350. begin
  351. if Source is TgxMaterialMultiProxy then
  352. MasterObjects := TgxMaterialMultiProxy(Source).MasterObjects;
  353. inherited;
  354. end;
  355. procedure TgxMaterialMultiProxy.DoRender(var rci: TgxRenderContextInfo;
  356. renderSelf, renderChildren: Boolean);
  357. var
  358. I: Integer;
  359. oldProxySubObject: Boolean;
  360. mpMaster: TgxMaterialMultiProxyMaster;
  361. d2: Single;
  362. begin
  363. if FRendering then
  364. Exit;
  365. FRendering := True;
  366. try
  367. d2 := VectorDistance2(rci.cameraPosition, AbsolutePosition);
  368. for I := 0 to MasterObjects.Count - 1 do
  369. begin
  370. mpMaster := MasterObjects[I];
  371. if (mpMaster.MasterObject <> nil) and (d2 >= mpMaster.FDistanceMin2) and
  372. (d2 < mpMaster.FDistanceMax2) then
  373. begin
  374. oldProxySubObject := rci.proxySubObject;
  375. rci.proxySubObject := True;
  376. with rci.PipelineTransformation do
  377. SetModelMatrix(MatrixMultiply(mpMaster.MasterObject.Matrix^, ModelMatrix^));
  378. if (mpMaster.MasterObject is TgxCustomSceneObject) and (FMaterialLibrary <> nil) then
  379. begin
  380. TgxCustomSceneObject(mpMaster.MasterObject).Material.QuickAssignMaterial(
  381. FMaterialLibrary, mpMaster.FMasterLibMaterial);
  382. end;
  383. mpMaster.MasterObject.DoRender(rci, renderSelf, (mpMaster.MasterObject.Count > 0));
  384. rci.proxySubObject := oldProxySubObject;
  385. end;
  386. end;
  387. // now render self stuff (our children, our effects, etc.)
  388. if renderChildren and (Count > 0) then
  389. Self.RenderChildren(0, Count - 1, rci);
  390. // if MasterGotEffects then
  391. // FMasterObject.Effects.RenderPostEffects(Scene.CurrentBuffer, rci);
  392. finally
  393. FRendering := False;
  394. end;
  395. ClearStructureChanged;
  396. end;
  397. function TgxMaterialMultiProxy.PrimaryMaster: TgxBaseSceneObject;
  398. begin
  399. if MasterObjects.Count > 0 then
  400. Result := MasterObjects[0].MasterObject
  401. else
  402. Result := nil;
  403. end;
  404. function TgxMaterialMultiProxy.AxisAlignedDimensionsUnscaled: TVector4f;
  405. var
  406. Master: TgxBaseSceneObject;
  407. begin
  408. Master := PrimaryMaster;
  409. if Assigned(Master) then
  410. Result := Master.AxisAlignedDimensionsUnscaled
  411. else
  412. Result := inherited AxisAlignedDimensionsUnscaled;
  413. end;
  414. function TgxMaterialMultiProxy.RayCastIntersect(const rayStart, rayVector: TVector4f;
  415. intersectPoint: PVector4f = nil; intersectNormal: PVector4f = nil): Boolean;
  416. var
  417. localRayStart, localRayVector: TVector4f;
  418. Master: TgxBaseSceneObject;
  419. begin
  420. Master := PrimaryMaster;
  421. if Assigned(Master) then
  422. begin
  423. SetVector(localRayStart, AbsoluteToLocal(rayStart));
  424. SetVector(localRayStart, Master.LocalToAbsolute(localRayStart));
  425. SetVector(localRayVector, AbsoluteToLocal(rayVector));
  426. SetVector(localRayVector, Master.LocalToAbsolute(localRayVector));
  427. NormalizeVector(localRayVector);
  428. Result := Master.RayCastIntersect(localRayStart, localRayVector,
  429. intersectPoint, intersectNormal);
  430. if Result then
  431. begin
  432. if Assigned(intersectPoint) then
  433. begin
  434. SetVector(intersectPoint^, Master.AbsoluteToLocal(intersectPoint^));
  435. SetVector(intersectPoint^, LocalToAbsolute(intersectPoint^));
  436. end;
  437. if Assigned(intersectNormal) then
  438. begin
  439. SetVector(intersectNormal^, Master.AbsoluteToLocal(intersectNormal^));
  440. SetVector(intersectNormal^, LocalToAbsolute(intersectNormal^));
  441. end;
  442. end;
  443. end
  444. else
  445. Result := False;
  446. end;
  447. function TgxMaterialMultiProxy.GenerateSilhouette(
  448. const silhouetteParameters: TgxSilhouetteParameters): TgxSilhouette;
  449. var
  450. Master: TgxBaseSceneObject;
  451. begin
  452. Master := PrimaryMaster;
  453. if Assigned(Master) then
  454. Result := Master.GenerateSilhouette(silhouetteParameters)
  455. else
  456. Result := nil;
  457. end;
  458. procedure TgxMaterialMultiProxy.SetMaterialLibrary(
  459. const Value: TgxMaterialLibrary);
  460. var
  461. I: Integer;
  462. begin
  463. if FMaterialLibrary <> Value then
  464. begin
  465. if FMaterialLibrary <> nil then
  466. FMaterialLibrary.RemoveFreeNotification(Self);
  467. FMaterialLibrary := Value;
  468. if FMaterialLibrary <> nil then
  469. begin
  470. FMaterialLibrary.FreeNotification(Self);
  471. if FMasterObjects.Count <> 0 then
  472. for I := 0 to FMasterObjects.Count - 1 do
  473. with FMasterObjects.GetItems(I) do
  474. begin
  475. if FTempLibMaterialName <> '' then
  476. SetMasterLibMaterialName(FTempLibMaterialName);
  477. end;
  478. end
  479. else
  480. begin
  481. if FMasterObjects.Count <> 0 then
  482. for I := 0 to FMasterObjects.Count - 1 do
  483. FMasterObjects.GetItems(I).FTempLibMaterialName := '';
  484. end;
  485. end;
  486. end;
  487. //-------------------------------------------------------------
  488. initialization
  489. //-------------------------------------------------------------
  490. RegisterClasses([TgxMaterialMultiProxyMaster, TgxMaterialMultiProxyMasters,
  491. TgxMaterialMultiProxy]);
  492. end.