GXS.Selection.pas 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.Selection;
  5. interface
  6. {$I Stage.Defines.inc}
  7. uses
  8. Winapi.OpenGL,
  9. System.SysUtils,
  10. System.Classes,
  11. GXS.Context,
  12. GXS.VectorLists,
  13. Stage.VectorGeometry,
  14. GXS.BaseClasses,
  15. GXS.PersistentClasses;
  16. const
  17. MAX_OBJECT_STACK_DEPTH = 512;
  18. type
  19. TPickSubObjects = array of LongInt;
  20. TPickRecord = class
  21. public
  22. AObject: TgxUpdateAbleComponent;
  23. SubObjects: TPickSubObjects;
  24. ZMin, ZMax: Single;
  25. end;
  26. TPickSortType = (psDefault, psName, psMinDepth, psMaxDepth);
  27. (* List class for object picking.
  28. This list is used to store the results of a PickObjects call. *)
  29. TgxPickList = class(TgxPersistentObjectList)
  30. private
  31. function GetFar(aValue: Integer): Single;
  32. function GetHit(aValue: Integer): TObject;
  33. function GetNear(aValue: Integer): Single;
  34. function GetSubObjects(aValue: Integer): TPickSubObjects;
  35. protected
  36. public
  37. constructor Create(aSortType: TPickSortType); reintroduce;
  38. procedure AddHit(obj: TObject; const subObj: TPickSubObjects;
  39. zMin, zMax: Single);
  40. procedure Clear; override;
  41. function FindObject(AObject: TObject): Integer;
  42. property FarDistance[Index: Integer]: Single read GetFar;
  43. property Hit[Index: Integer]: TObject read GetHit; default;
  44. property NearDistance[Index: Integer]: Single read GetNear;
  45. property SubObjects[Index: Integer]: TPickSubObjects read GetSubObjects;
  46. end;
  47. TgxBaseSelectTechnique = class
  48. protected
  49. FObjectStack: array of TObject;
  50. FNameStack: array[0..255] of Cardinal;
  51. FCurrentName: Cardinal;
  52. FStackPosition: Integer;
  53. FObjectCountGuess: Integer;
  54. FHits: Integer;
  55. function GetObject: TObject; virtual; abstract;
  56. procedure SetObject(Value: TObject); virtual; abstract;
  57. function GetHits: Integer; virtual; abstract;
  58. procedure SetHits(Value: Integer); virtual; abstract;
  59. procedure SetObjectCountGuess(Value: Integer); virtual; abstract;
  60. function GetItems(Value: Integer): TObject; virtual; abstract;
  61. public
  62. class function IsSupported: Boolean; virtual; abstract;
  63. procedure Start; virtual; abstract;
  64. function Stop: Boolean; virtual; abstract;
  65. procedure PushObject(AName: TObject); virtual; abstract;
  66. procedure PopObject(); virtual; abstract;
  67. procedure LoadObject(AName: TObject); virtual; abstract;
  68. procedure FillPickingList(var AList: TgxPickList); virtual; abstract;
  69. property CurrentObject: TObject read GetObject write SetObject;
  70. property ObjectCountGuess: Integer read FObjectCountGuess write SetObjectCountGuess;
  71. property Hits: Integer read GetHits write SetHits;
  72. end;
  73. TgxBaseSelectTechniqueClass = class of TgxBaseSelectTechnique;
  74. TgxSelectRenderModeTechnique = class(TgxBaseSelectTechnique)
  75. private
  76. FBuffer: array of Cardinal;
  77. protected
  78. function GetObject: TObject; override;
  79. procedure SetObject(Value: TObject); override;
  80. function GetHits: Integer; override;
  81. procedure SetHits(Value: Integer); override;
  82. procedure SetObjectCountGuess(Value: Integer); override;
  83. public
  84. class function IsSupported: Boolean; override;
  85. procedure Start; override;
  86. function Stop: Boolean; override;
  87. procedure FillPickingList(var AList: TgxPickList); override;
  88. property ObjectCountGuess;
  89. property Hits;
  90. property CurrentObject;
  91. end;
  92. function GetBestSelectorClass: TgxBaseSelectTechniqueClass;
  93. //------------------------------------------------
  94. implementation
  95. //------------------------------------------------
  96. function GetBestSelectorClass: TgxBaseSelectTechniqueClass;
  97. begin
  98. // if TgxSelectRenderToTextureTechnique.IsSupported then
  99. // Result := TgxSelectRenderToTextureTechnique
  100. // else
  101. Result := TgxSelectRenderModeTechnique;
  102. end;
  103. {$IFDEF USE_REGIONS}{$REGION 'TgxPickList'}{$ENDIF}
  104. // ------------------
  105. // ------------------ TgxPickList ------------------
  106. // ------------------
  107. var
  108. vPickListSortFlag: TPickSortType;
  109. // Create
  110. //
  111. constructor TgxPickList.Create(aSortType: TPickSortType);
  112. begin
  113. vPickListSortFlag := aSortType;
  114. inherited Create;
  115. end;
  116. // Comparefunction (for picklist sorting)
  117. //
  118. function Comparefunction(item1, item2: TObject): Integer;
  119. var
  120. diff: Single;
  121. begin
  122. Result := 0;
  123. case vPickListSortFlag of
  124. psName:
  125. Result := CompareText(TComponent(TPickRecord(Item1).AObject).Name,
  126. TComponent(TPickRecord(Item1).AObject).Name);
  127. psMinDepth:
  128. begin
  129. Diff := TPickRecord(Item1).ZMin - TPickRecord(Item2).ZMin;
  130. if Diff < 0 then
  131. Result := -1
  132. else if Diff > 0 then
  133. Result := 1
  134. else
  135. Result := 0;
  136. end;
  137. psMaxDepth:
  138. begin
  139. Diff := TPickRecord(Item1).ZMax - TPickRecord(Item2).ZMax;
  140. if Diff < 0 then
  141. Result := -1
  142. else if Diff > 0 then
  143. Result := 1
  144. else
  145. Result := 0;
  146. end;
  147. end;
  148. end;
  149. // AddHit
  150. //
  151. procedure TgxPickList.AddHit(obj: TObject;
  152. const subObj: TPickSubObjects; zMin, zMax: Single);
  153. var
  154. newRecord: TPickRecord;
  155. begin
  156. newRecord := TPickRecord.Create;
  157. newRecord.AObject := TgxUpdateAbleComponent(obj);
  158. newRecord.SubObjects := subObj;
  159. newRecord.zMin := zMin;
  160. newRecord.zMax := zMax;
  161. Add(newRecord);
  162. if vPickListSortFlag <> psDefault then
  163. Sort(@Comparefunction);
  164. end;
  165. // Clear
  166. //
  167. procedure TgxPickList.Clear;
  168. begin
  169. DoClean;
  170. inherited;
  171. end;
  172. // FindObject
  173. //
  174. function TgxPickList.FindObject(aObject: TObject): Integer;
  175. var
  176. i: Integer;
  177. begin
  178. Result := -1;
  179. if Assigned(AObject) then
  180. for i := 0 to Count - 1 do
  181. begin
  182. if Hit[i] = AObject then
  183. begin
  184. Result := i;
  185. Break;
  186. end;
  187. end;
  188. end;
  189. // GetFar
  190. //
  191. function TgxPickList.GetFar(aValue: Integer): Single;
  192. begin
  193. Result := TPickRecord(Items[AValue]).ZMax;
  194. end;
  195. // GetHit
  196. //
  197. function TgxPickList.GetHit(aValue: Integer): TObject;
  198. begin
  199. Result := TPickRecord(Items[AValue]).AObject;
  200. end;
  201. // GetNear
  202. //
  203. function TgxPickList.GetNear(aValue: Integer): Single;
  204. begin
  205. Result := TPickRecord(Items[AValue]).ZMin;
  206. end;
  207. // GetSubObjects
  208. //
  209. function TgxPickList.GetSubObjects(aValue: Integer): TPickSubobjects;
  210. begin
  211. Result := TPickRecord(Items[AValue]).SubObjects;
  212. end;
  213. {$IFDEF USE_REGIONS}{$ENDREGION}{$ENDIF}
  214. {$IFDEF USE_REGIONS}{$REGION 'TgxSelectRenderModeTechnique'}{$ENDIF}
  215. // ------------------
  216. // ------------------ TgxSelectRenderModeTechnique ------------------
  217. // ------------------
  218. function TgxSelectRenderModeTechnique.GetHits: Integer;
  219. begin
  220. Result := FHits;
  221. end;
  222. procedure TgxSelectRenderModeTechnique.SetHits(Value: Integer);
  223. begin
  224. FHits := Value;
  225. end;
  226. procedure TgxSelectRenderModeTechnique.SetObjectCountGuess(Value: Integer);
  227. begin
  228. if Value<8 then
  229. Value := 8;
  230. FObjectCountGuess := Value;
  231. end;
  232. class function TgxSelectRenderModeTechnique.IsSupported: Boolean;
  233. begin
  234. Result := GL_VERSION = 1.1;
  235. end;
  236. procedure TgxSelectRenderModeTechnique.Start;
  237. begin
  238. SetLength(FBuffer, FObjectCountGuess * 4 + 32);
  239. glSelectBuffer(FObjectCountGuess * SizeOf(GLuint), @FBuffer[0]);
  240. glRenderMode(GL_SELECT);
  241. glInitNames;
  242. FCurrentName := 0;
  243. SetLength(FObjectStack, MAX_OBJECT_STACK_DEPTH);
  244. FStackPosition := 0;
  245. glPushName(0);
  246. end;
  247. function TgxSelectRenderModeTechnique.Stop: Boolean;
  248. begin
  249. glFlush;
  250. FHits := glRenderMode(GL_RENDER);
  251. Result := FHits > -1;
  252. if not Result then
  253. Inc(FObjectCountGuess);
  254. end;
  255. procedure TgxSelectRenderModeTechnique.FillPickingList(var AList: TgxPickList);
  256. var
  257. subObj: TPickSubObjects;
  258. next, current, subObjIndex: Cardinal;
  259. szmin, szmax: Single;
  260. I: Integer;
  261. begin
  262. if not Assigned(AList) then
  263. AList := TgxPickList.Create(psDefault)
  264. else
  265. AList.Clear;
  266. if FHits > -1 then
  267. begin
  268. next := 0;
  269. for I := 0 to FHits - 1 do
  270. begin
  271. current := next;
  272. next := current + FBuffer[current] + 3;
  273. szmin := (FBuffer[current + 1] shr 1) * (1 / MaxInt);
  274. szmax := (FBuffer[current + 2] shr 1) * (1 / MaxInt);
  275. subObj := nil;
  276. subObjIndex := current + 4;
  277. if subObjIndex < next then
  278. begin
  279. SetLength(subObj, FBuffer[current] - 1);
  280. while subObjIndex < next do
  281. begin
  282. subObj[subObjIndex - current - 4] := FBuffer[subObjIndex];
  283. inc(subObjIndex);
  284. end;
  285. end;
  286. AList.AddHit(FObjectStack[FBuffer[current + 3]], subObj, szmin, szmax);
  287. end;
  288. end;
  289. // Restore initial object stack length
  290. SetLength(FObjectStack, MAX_OBJECT_STACK_DEPTH);
  291. end;
  292. function TgxSelectRenderModeTechnique.GetObject: TObject;
  293. begin
  294. Result := FObjectStack[FCurrentName];
  295. end;
  296. procedure TgxSelectRenderModeTechnique.SetObject(Value: TObject);
  297. begin
  298. // Grow Object stack length if needed
  299. if FCurrentName >= Length(FObjectStack) then
  300. SetLength(FObjectStack, Length(FObjectStack) * 2);
  301. FObjectStack[FCurrentName] := Value;
  302. glLoadName(FCurrentName);
  303. Inc(FCurrentName);
  304. end;
  305. {$IFDEF USE_REGIONS}{$ENDREGION}{$ENDIF}
  306. end.