GXS.ObjectManager.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.ObjectManager;
  5. (*
  6. The object manager is used for registering classes together with a category,
  7. description + icon, so that they can be displayed visually. This can then
  8. be used by run-time or design-time scene editors for choosing which
  9. scene objects to place into a scene.
  10. TODO: add some notification code, so that when a scene object is registered/
  11. unregistered, any editor that is using the object manager can be notified.
  12. *)
  13. interface
  14. {$I Stage.Defines.inc}
  15. uses
  16. System.Classes,
  17. System.SysUtils,
  18. System.UITypes,
  19. FMX.Graphics,
  20. FMX.Controls,
  21. FMX.Menus,
  22. GXS.ImageUtils,
  23. GXS.Scene;
  24. type
  25. PSceneObjectEntry = ^TgxSceneObjectEntry;
  26. // holds a relation between an scene object class, its global identification,
  27. // its location in the object stock and its icon reference
  28. TgxSceneObjectEntry = record
  29. ObjectClass: TgxSceneObjectClass;
  30. Name: string; // type name of the object
  31. Category: string; // category of object
  32. Index, // index into "FSceneObjectList"
  33. ImageIndex: Integer; // index into "FObjectIcons"
  34. end;
  35. TgxObjectManager = class(TComponent)
  36. private
  37. FSceneObjectList: TList;
  38. FObjectIcons: TStyleBook; // In VCL FObjectIcons: TImageList; <- a list of icons for scene objects
  39. {$IFDEF MSWINDOWS}
  40. FOverlayIndex, // indices into the object icon list
  41. {$ENDIF}
  42. FSceneRootIndex,
  43. FCameraRootIndex,
  44. FLightsourceRootIndex,
  45. FObjectRootIndex: Integer;
  46. protected
  47. procedure DestroySceneObjectList;
  48. function FindSceneObjectClass(AObjectClass: TgxSceneObjectClass;
  49. const ASceneObject: string = ''): PSceneObjectEntry;
  50. public
  51. constructor Create(AOwner: TComponent); override;
  52. destructor Destroy; override;
  53. procedure CreateDefaultObjectIcons(ResourceModule: Cardinal);
  54. function GetClassFromIndex(Index: Integer): TgxSceneObjectClass;
  55. function GetImageIndex(ASceneObject: TgxSceneObjectClass): Integer;
  56. function GetCategory(ASceneObject: TgxSceneObjectClass): string;
  57. procedure GetRegisteredSceneObjects(ObjectList: TStringList);
  58. procedure PopulateMenuWithRegisteredSceneObjects(AMenuItem: TMenuItem; AClickEvent: TNotifyEvent);
  59. // Registers a stock object and adds it to the stock object list
  60. procedure RegisterSceneObject(ASceneObject: TgxSceneObjectClass; const AName, ACategory: string); overload;
  61. procedure RegisterSceneObject(ASceneObject: TgxSceneObjectClass; const AName, ACategory: string; ABitmap: TBitmap); overload;
  62. procedure RegisterSceneObject(ASceneObject: TgxSceneObjectClass; const AName, ACategory: string; ResourceModule: Cardinal; ResourceName: string = ''); overload;
  63. // Unregisters a stock object and removes it from the stock object list
  64. procedure UnRegisterSceneObject(ASceneObject: TgxSceneObjectClass);
  65. property ObjectIcons: TStyleBook read FObjectIcons; //In VCL TImageList
  66. property SceneRootIndex: Integer read FSceneRootIndex;
  67. property LightsourceRootIndex: Integer read FLightsourceRootIndex;
  68. property CameraRootIndex: Integer read FCameraRootIndex;
  69. property ObjectRootIndex: Integer read FObjectRootIndex;
  70. end;
  71. // ------------------------------------------------------------------
  72. implementation
  73. // ------------------------------------------------------------------
  74. //----------------- TgxObjectManager ---------------------------------------------
  75. constructor TgxObjectManager.Create(AOwner: TComponent);
  76. begin
  77. inherited;
  78. FSceneObjectList := TList.Create;
  79. // FObjectIcons Width + Height are set when you add the first bitmap
  80. FObjectIcons := TStyleBook.Create(AOwner); //In VCL TImageList.CreateSize(16, 16);
  81. end;
  82. destructor TgxObjectManager.Destroy;
  83. begin
  84. DestroySceneObjectList;
  85. FObjectIcons.Free;
  86. inherited Destroy;
  87. end;
  88. function TgxObjectManager.FindSceneObjectClass(AObjectClass: TgxSceneObjectClass;
  89. const aSceneObject: string = ''): PSceneObjectEntry;
  90. var
  91. I: Integer;
  92. Found: Boolean;
  93. begin
  94. Result := nil;
  95. Found := False;
  96. with FSceneObjectList do
  97. begin
  98. for I := 0 to Count - 1 do
  99. with TgxSceneObjectEntry(Items[I]^) do
  100. if (ObjectClass = AObjectClass) and (Length(ASceneObject) = 0)
  101. or (CompareText(Name, ASceneObject) = 0) then
  102. begin
  103. Found := True;
  104. Break;
  105. end;
  106. if Found then
  107. Result := Items[I];
  108. end;
  109. end;
  110. function TgxObjectManager.GetClassFromIndex(Index: Integer): TgxSceneObjectClass;
  111. begin
  112. if Index < 0 then
  113. Index := 0;
  114. if Index > FSceneObjectList.Count - 1 then
  115. Index := FSceneObjectList.Count - 1;
  116. Result := TgxSceneObjectEntry(FSceneObjectList.Items[Index + 1]^).ObjectClass;
  117. end;
  118. function TgxObjectManager.GetImageIndex(ASceneObject: TgxSceneObjectClass): Integer;
  119. var
  120. classEntry: PSceneObjectEntry;
  121. begin
  122. classEntry := FindSceneObjectClass(ASceneObject);
  123. if Assigned(classEntry) then
  124. Result := classEntry^.ImageIndex
  125. else
  126. Result := 0;
  127. end;
  128. function TgxObjectManager.GetCategory(ASceneObject: TgxSceneObjectClass): string;
  129. var
  130. classEntry: PSceneObjectEntry;
  131. begin
  132. classEntry := FindSceneObjectClass(ASceneObject);
  133. if Assigned(classEntry) then
  134. Result := classEntry^.Category
  135. else
  136. Result := '';
  137. end;
  138. procedure TgxObjectManager.GetRegisteredSceneObjects(objectList: TStringList);
  139. var
  140. i: Integer;
  141. begin
  142. if Assigned(objectList) then
  143. with objectList do
  144. begin
  145. Clear;
  146. for i := 0 to FSceneObjectList.Count - 1 do
  147. with TgxSceneObjectEntry(FSceneObjectList.Items[I]^) do
  148. AddObject(Name, Pointer(ObjectClass));
  149. end;
  150. end;
  151. procedure TgxObjectManager.PopulateMenuWithRegisteredSceneObjects(AMenuItem: TMenuItem;
  152. AClickEvent: TNotifyEvent);
  153. var
  154. ObjectList: TStringList;
  155. i, j: Integer;
  156. Item, CurrentParent: TMenuItem;
  157. CurrentCategory: string;
  158. Soc: TgxSceneObjectClass;
  159. begin
  160. ObjectList := TStringList.Create;
  161. try
  162. GetRegisteredSceneObjects(ObjectList);
  163. for i := 0 to ObjectList.Count - 1 do
  164. if ObjectList[i] <> '' then
  165. begin
  166. CurrentCategory := GetCategory(TgxSceneObjectClass(ObjectList.Objects[i]));
  167. if CurrentCategory = '' then
  168. CurrentParent := AMenuItem
  169. else
  170. begin
  171. CurrentParent := TMenuItem.Create(nil);
  172. CurrentParent.Text := ObjectList[j];
  173. //in VCL CurrentParent := NewItem(CurrentCategory, 0, False, True, nil, 0, '');
  174. AMenuItem.AddObject(CurrentParent);
  175. end;
  176. for j := i to ObjectList.Count - 1 do
  177. if ObjectList[j] <> '' then
  178. begin
  179. Soc := TgxSceneObjectClass(ObjectList.Objects[j]);
  180. if CurrentCategory = GetCategory(Soc) then
  181. begin
  182. Item := TMenuItem.Create(nil);
  183. Item.Text := ObjectList[j];
  184. //in VCL Item := NewItem(ObjectList[j], 0, False, True, AClickEvent, 0, '');
  185. { TODO : E2003 Undeclared identifier: 'ImageIndex' }
  186. (*Item.ImageIndex := GetImageIndex(Soc);*)
  187. CurrentParent.AddObject(Item);
  188. ObjectList[j] := '';
  189. if CurrentCategory = '' then
  190. Break;
  191. end;
  192. end;
  193. end;
  194. finally
  195. ObjectList.Free;
  196. end;
  197. end;
  198. procedure TgxObjectManager.RegisterSceneObject(ASceneObject: TgxSceneObjectClass;
  199. const aName, aCategory: string);
  200. var
  201. resBitmapName: string;
  202. bmp: TBitmap;
  203. begin
  204. // Since no resource name was provided, assume it's the same as class name
  205. resBitmapName := ASceneObject.ClassName;
  206. bmp := TBitmap.Create;
  207. try
  208. // Try loading bitmap from module that class is in
  209. GLLoadBitmapFromInstance(FindClassHInstance(ASceneObject), bmp, resBitmapName);
  210. if bmp.Width = 0 then
  211. GLLoadBitmapFromInstance(HInstance, bmp, resBitmapName);
  212. // If resource was found, register scene object with bitmap
  213. if bmp.Width <> 0 then
  214. begin
  215. RegisterSceneObject(ASceneObject, aName, aCategory, bmp);
  216. end
  217. else
  218. // Resource not found, so register without bitmap
  219. RegisterSceneObject(ASceneObject, aName, aCategory, nil);
  220. finally
  221. bmp.Free;
  222. end;
  223. end;
  224. procedure TgxObjectManager.RegisterSceneObject(ASceneObject: TgxSceneObjectClass; const AName, ACategory: string; ABitmap: TBitmap);
  225. var
  226. NewEntry: PSceneObjectEntry;
  227. bmp: TBitmap;
  228. begin
  229. if Assigned(RegisterNoIconProc) then
  230. RegisterNoIcon([aSceneObject]);
  231. with FSceneObjectList do
  232. begin
  233. // make sure no class is registered twice
  234. if Assigned(FindSceneObjectClass(ASceneObject, AName)) then
  235. Exit;
  236. New(NewEntry);
  237. try
  238. with NewEntry^ do
  239. begin
  240. // object stock stuff
  241. // registered objects list stuff
  242. ObjectClass := ASceneObject;
  243. NewEntry^.Name := aName;
  244. NewEntry^.Category := aCategory;
  245. Index := FSceneObjectList.Count;
  246. if Assigned(aBitmap) then
  247. begin
  248. bmp := TBitmap.Create;
  249. try
  250. // If we just add the bitmap, and it has different dimensions, then
  251. // all icons will be cleared, so ensure this doesn't happen
  252. { TODO : E2129 Cannot assign to a read-only property }
  253. (*bmp.PixelFormat := TPixelFormat.RGBA; //in VCL glpf24bit;*)
  254. { TODO : E2003 Undeclared identifier: 'Width', 'Height'}
  255. (*
  256. bmp.Width := FObjectIcons.Width;
  257. bmp.Height := FObjectIcons.Height;
  258. *)
  259. { TODO : E2003 Undeclared identifiers: 'SrcRect' etc.}
  260. (*bmp.Canvas.DrawBitmap(ABitmap, SrcRect, DstRect, AOpacity, HighSpeed);*)
  261. //in VCL bmp.Canvas.Draw(0, 0, ABitmap);
  262. { TODO : E2003 Undeclared identifier: 'AddMasked' }
  263. (*FObjectIcons.AddMasked(bmp, bmp.Canvas.Pixels[0, 0]);*)
  264. ImageIndex := FObjectIcons.Index - 1; //in VCL FObjectIcons.Count
  265. finally
  266. bmp.free;
  267. end;
  268. end
  269. else
  270. ImageIndex := 0;
  271. end;
  272. Add(NewEntry);
  273. finally
  274. //
  275. end;
  276. end;
  277. end;
  278. procedure TgxObjectManager.RegisterSceneObject(ASceneObject: TgxSceneObjectClass; const aName, aCategory: string; ResourceModule: Cardinal; ResourceName: string = '');
  279. var
  280. bmp: TBitmap;
  281. resBitmapName: string;
  282. begin
  283. if ResourceName = '' then
  284. resBitmapName := ASceneObject.ClassName
  285. else
  286. resBitmapName := ResourceName;
  287. bmp := TBitmap.Create;
  288. try
  289. // Load resource
  290. if (ResourceModule <> 0) then
  291. GLLoadBitmapFromInstance(ResourceModule, bmp, resBitmapName);
  292. // If the resource was found, then register scene object using the bitmap
  293. if bmp.Width > 0 then
  294. RegisterSceneObject(ASceneObject, aName, aCategory, bmp)
  295. else
  296. // Register the scene object with no icon
  297. RegisterSceneObject(ASceneObject, aName, aCategory, nil);
  298. finally
  299. bmp.Free;
  300. end;
  301. end;
  302. procedure TgxObjectManager.UnRegisterSceneObject(ASceneObject: TgxSceneObjectClass);
  303. var
  304. oldEntry: PSceneObjectEntry;
  305. begin
  306. // find the class in the scene object list
  307. OldEntry := FindSceneObjectClass(ASceneObject);
  308. // found?
  309. if assigned(OldEntry) then
  310. begin
  311. // remove its entry from the list of registered objects
  312. FSceneObjectList.Remove(OldEntry);
  313. // finally free the memory for the entry
  314. Dispose(OldEntry);
  315. end;
  316. end;
  317. procedure TgxObjectManager.CreateDefaultObjectIcons(ResourceModule: Cardinal);
  318. var
  319. bmp: TBitmap;
  320. begin
  321. bmp := TBitmap.Create;
  322. with FObjectIcons, bmp.Canvas do
  323. begin
  324. try
  325. // There's a more direct way for loading images into the image list, but
  326. // the image quality suffers too much
  327. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_root');
  328. { TODO : E2003 Undeclared identifier: 'AddMasked' }
  329. (*FSceneRootIndex := AddMasked(bmp, Pixels[0, 0]);*)
  330. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_camera');
  331. { TODO : E2003 Undeclared identifier: 'AddMasked' }
  332. (*FCameraRootIndex := AddMasked(bmp, Pixels[0, 0]);*)
  333. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_lights');
  334. { TODO : E2003 Undeclared identifier: 'AddMasked' }
  335. (*FLightsourceRootIndex := AddMasked(bmp, Pixels[0, 0]);*)
  336. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_objects');
  337. { TODO : E2003 Undeclared identifier: 'AddMasked' }
  338. (*FObjectRootIndex := AddMasked(bmp, Pixels[0, 0]);*)
  339. finally
  340. bmp.Free;
  341. end;
  342. end;
  343. end;
  344. procedure TgxObjectManager.DestroySceneObjectList;
  345. var
  346. i: Integer;
  347. begin
  348. with FSceneObjectList do
  349. begin
  350. for i := 0 to Count - 1 do
  351. Dispose(PSceneObjectEntry(Items[I]));
  352. Free;
  353. end;
  354. end;
  355. initialization
  356. end.