GLObjectManager.pas 12 KB

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