GLS.ObjectManager.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.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. VCL.Graphics,
  19. VCL.Controls,
  20. VCL.Menus,
  21. GLS.Scene,
  22. GLS.ImageUtils,
  23. Stage.Utils;
  24. type
  25. PSceneObjectEntry = ^TGLSceneObjectEntry;
  26. // holds a relation between an scene object class, its global identification,
  27. // its location in the object stock and its icon reference
  28. TGLSceneObjectEntry = record
  29. ObjectClass: TGLSceneObjectClass;
  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. TGLObjectManager = class(TComponent)
  36. private
  37. FSceneObjectList: TList;
  38. FObjectIcons: TImageList; // a list of icons for scene objects
  39. FOverlayIndex, // indices into the object icon list
  40. FSceneRootIndex, FCameraRootIndex, FLightsourceRootIndex,
  41. FObjectRootIndex: Integer;
  42. protected
  43. procedure DestroySceneObjectList;
  44. function FindSceneObjectClass(AObjectClass: TGLSceneObjectClass;
  45. const ASceneObject: string = ''): PSceneObjectEntry;
  46. public
  47. constructor Create(AOwner: TComponent); override;
  48. destructor Destroy; override;
  49. procedure CreateDefaultObjectIcons(ResourceModule: Cardinal);
  50. function GetClassFromIndex(Index: Integer): TGLSceneObjectClass;
  51. function GetImageIndex(ASceneObject: TGLSceneObjectClass): Integer;
  52. function GetCategory(ASceneObject: TGLSceneObjectClass): string;
  53. procedure GetRegisteredSceneObjects(ObjectList: TStringList);
  54. procedure PopulateMenuWithRegisteredSceneObjects(AMenuItem: TMenuItem;
  55. 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;
  63. ResourceName: string = ''); overload;
  64. // Unregisters a stock object and removes it from the stock object list
  65. procedure UnRegisterSceneObject(ASceneObject: TGLSceneObjectClass);
  66. property ObjectIcons: TImageList read FObjectIcons;
  67. property SceneRootIndex: Integer read FSceneRootIndex;
  68. property LightsourceRootIndex: Integer read FLightsourceRootIndex;
  69. property CameraRootIndex: Integer read FCameraRootIndex;
  70. property ObjectRootIndex: Integer read FObjectRootIndex;
  71. end;
  72. // ------------------------------------------------------------------
  73. implementation
  74. // ------------------------------------------------------------------
  75. constructor TGLObjectManager.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 := TImageList.CreateSize(16, 16);
  81. end;
  82. destructor TGLObjectManager.Destroy;
  83. begin
  84. DestroySceneObjectList;
  85. FObjectIcons.Free;
  86. inherited Destroy;
  87. end;
  88. function TGLObjectManager.FindSceneObjectClass(AObjectClass
  89. : TGLSceneObjectClass; 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 TGLSceneObjectEntry(Items[I]^) do
  100. if (ObjectClass = AObjectClass) and (Length(ASceneObject) = 0) or
  101. (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 TGLObjectManager.GetClassFromIndex(Index: Integer)
  111. : TGLSceneObjectClass;
  112. begin
  113. if Index < 0 then
  114. Index := 0;
  115. if Index > FSceneObjectList.Count - 1 then
  116. Index := FSceneObjectList.Count - 1;
  117. Result := TGLSceneObjectEntry(FSceneObjectList.Items[Index + 1]^).ObjectClass;
  118. end;
  119. function TGLObjectManager.GetImageIndex(ASceneObject
  120. : TGLSceneObjectClass): Integer;
  121. var
  122. classEntry: PSceneObjectEntry;
  123. begin
  124. classEntry := FindSceneObjectClass(ASceneObject);
  125. if Assigned(classEntry) then
  126. Result := classEntry^.ImageIndex
  127. else
  128. Result := 0;
  129. end;
  130. function TGLObjectManager.GetCategory(ASceneObject
  131. : TGLSceneObjectClass): string;
  132. var
  133. classEntry: PSceneObjectEntry;
  134. begin
  135. classEntry := FindSceneObjectClass(ASceneObject);
  136. if Assigned(classEntry) then
  137. Result := classEntry^.Category
  138. else
  139. Result := '';
  140. end;
  141. procedure TGLObjectManager.GetRegisteredSceneObjects(ObjectList: TStringList);
  142. var
  143. I: Integer;
  144. begin
  145. if Assigned(ObjectList) then
  146. with ObjectList do
  147. begin
  148. Clear;
  149. for I := 0 to FSceneObjectList.Count - 1 do
  150. with TGLSceneObjectEntry(FSceneObjectList.Items[I]^) do
  151. AddObject(Name, Pointer(ObjectClass));
  152. end;
  153. end;
  154. procedure TGLObjectManager.PopulateMenuWithRegisteredSceneObjects
  155. (AMenuItem: TMenuItem; aClickEvent: TNotifyEvent);
  156. var
  157. ObjectList: TStringList;
  158. I, j: Integer;
  159. Item, CurrentParent: TMenuItem;
  160. CurrentCategory: string;
  161. Soc: TGLSceneObjectClass;
  162. begin
  163. ObjectList := TStringList.Create;
  164. try
  165. GetRegisteredSceneObjects(ObjectList);
  166. for I := 0 to ObjectList.Count - 1 do
  167. if ObjectList[I] <> '' then
  168. begin
  169. CurrentCategory :=
  170. GetCategory(TGLSceneObjectClass(ObjectList.Objects[I]));
  171. if CurrentCategory = '' then
  172. CurrentParent := AMenuItem
  173. else
  174. begin
  175. CurrentParent := NewItem(CurrentCategory, 0, False, True, nil, 0, '');
  176. AMenuItem.Add(CurrentParent);
  177. end;
  178. for j := I to ObjectList.Count - 1 do
  179. if ObjectList[j] <> '' then
  180. begin
  181. Soc := TGLSceneObjectClass(ObjectList.Objects[j]);
  182. if CurrentCategory = GetCategory(Soc) then
  183. begin
  184. Item := NewItem(ObjectList[j], 0, False, True,
  185. aClickEvent, 0, '');
  186. Item.ImageIndex := GetImageIndex(Soc);
  187. CurrentParent.Add(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 TGLObjectManager.RegisterSceneObject(ASceneObject
  199. : TGLSceneObjectClass; 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,
  210. resBitmapName);
  211. if bmp.Width = 0 then
  212. GLLoadBitmapFromInstance(HInstance, bmp, resBitmapName);
  213. // If resource was found, register scene object with bitmap
  214. if bmp.Width <> 0 then
  215. begin
  216. RegisterSceneObject(ASceneObject, aName, aCategory, bmp);
  217. end
  218. else
  219. // Resource not found, so register without bitmap
  220. RegisterSceneObject(ASceneObject, aName, aCategory, nil);
  221. finally
  222. bmp.Free;
  223. end;
  224. end;
  225. procedure TGLObjectManager.RegisterSceneObject(ASceneObject
  226. : TGLSceneObjectClass; const aName, aCategory: string; aBitmap: TBitmap);
  227. var
  228. newEntry: PSceneObjectEntry;
  229. bmp: TBitmap;
  230. begin
  231. if Assigned(RegisterNoIconProc) then
  232. RegisterNoIcon([ASceneObject]);
  233. with FSceneObjectList do
  234. begin
  235. // make sure no class is registered twice
  236. if Assigned(FindSceneObjectClass(ASceneObject, aName)) then
  237. Exit;
  238. New(newEntry);
  239. try
  240. with newEntry^ do
  241. begin
  242. // object stock stuff
  243. // registered objects list stuff
  244. ObjectClass := ASceneObject;
  245. newEntry^.Name := aName;
  246. newEntry^.Category := aCategory;
  247. Index := FSceneObjectList.Count;
  248. if Assigned(aBitmap) then
  249. begin
  250. bmp := TBitmap.Create;
  251. try
  252. // If we just add the bitmap, and it has different dimensions, then
  253. // all icons will be cleared, so ensure this doesn't happen
  254. bmp.PixelFormat := pf24bit;
  255. bmp.Width := FObjectIcons.Width;
  256. bmp.Height := FObjectIcons.Height;
  257. bmp.Canvas.Draw(0, 0, aBitmap);
  258. FObjectIcons.AddMasked(bmp, bmp.Canvas.Pixels[0, 0]);
  259. ImageIndex := FObjectIcons.Count - 1;
  260. finally
  261. bmp.Free;
  262. end;
  263. end
  264. else
  265. ImageIndex := 0;
  266. end;
  267. Add(newEntry);
  268. finally
  269. //
  270. end;
  271. end;
  272. end;
  273. procedure TGLObjectManager.RegisterSceneObject(ASceneObject
  274. : TGLSceneObjectClass; const aName, aCategory: string;
  275. ResourceModule: Cardinal; ResourceName: string = '');
  276. var
  277. bmp: TBitmap;
  278. resBitmapName: string;
  279. begin
  280. if ResourceName = '' then
  281. resBitmapName := ASceneObject.ClassName
  282. else
  283. resBitmapName := ResourceName;
  284. bmp := TBitmap.Create;
  285. try
  286. // Load resource
  287. if (ResourceModule <> 0) then
  288. GLLoadBitmapFromInstance(ResourceModule, bmp, resBitmapName);
  289. // If the resource was found, then register scene object using the bitmap
  290. if bmp.Width > 0 then
  291. RegisterSceneObject(ASceneObject, aName, aCategory, bmp)
  292. else
  293. // Register the scene object with no icon
  294. RegisterSceneObject(ASceneObject, aName, aCategory, nil);
  295. finally
  296. bmp.Free;
  297. end;
  298. end;
  299. procedure TGLObjectManager.UnRegisterSceneObject(ASceneObject
  300. : TGLSceneObjectClass);
  301. var
  302. oldEntry: PSceneObjectEntry;
  303. begin
  304. // find the class in the scene object list
  305. oldEntry := FindSceneObjectClass(ASceneObject);
  306. // found?
  307. if Assigned(oldEntry) then
  308. begin
  309. // remove its entry from the list of registered objects
  310. FSceneObjectList.Remove(oldEntry);
  311. // finally free the memory for the entry
  312. Dispose(oldEntry);
  313. end;
  314. end;
  315. procedure TGLObjectManager.CreateDefaultObjectIcons(ResourceModule: Cardinal);
  316. var
  317. bmp: TBitmap;
  318. begin
  319. bmp := TBitmap.Create;
  320. with FObjectIcons, bmp.Canvas do
  321. begin
  322. try
  323. // There's a more direct way for loading images into the image list, but
  324. // the image quality suffers too much
  325. {$IFDEF WIN32}
  326. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_cross');
  327. FOverlayIndex := AddMasked(bmp, Pixels[0, 0]);
  328. Overlay(FOverlayIndex, 0); // used as indicator for disabled objects
  329. {$ENDIF}
  330. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_root');
  331. FSceneRootIndex := AddMasked(bmp, Pixels[0, 0]);
  332. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_camera');
  333. FCameraRootIndex := AddMasked(bmp, Pixels[0, 0]);
  334. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_lights');
  335. FLightsourceRootIndex := AddMasked(bmp, Pixels[0, 0]);
  336. GLLoadBitmapFromInstance(ResourceModule, bmp, 'gls_objects');
  337. FObjectRootIndex := AddMasked(bmp, Pixels[0, 0]);
  338. finally
  339. bmp.Free;
  340. end;
  341. end;
  342. end;
  343. procedure TGLObjectManager.DestroySceneObjectList;
  344. var
  345. I: Integer;
  346. begin
  347. with FSceneObjectList do
  348. begin
  349. for I := 0 to Count - 1 do
  350. Dispose(PSceneObjectEntry(Items[I]));
  351. Free;
  352. end;
  353. end;
  354. // -------------------------------------------
  355. initialization
  356. // -------------------------------------------
  357. end.