GLS.Selection.pas 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Selection;
  5. (* Picking and selection of objects *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.SysUtils,
  11. System.Classes,
  12. GLS.OpenGLTokens,
  13. GLS.Context,
  14. GLS.VectorLists,
  15. GLS.VectorGeometry,
  16. GLS.BaseClasses,
  17. GLS.PersistentClasses;
  18. const
  19. MAX_OBJECT_STACK_DEPTH = 512;
  20. type
  21. TPickSubObjects = array of LongInt;
  22. TPickRecord = class
  23. public
  24. AObject: TGLUpdateAbleComponent;
  25. SubObjects: TPickSubObjects;
  26. ZMin, ZMax: Single;
  27. end;
  28. TPickSortType = (psDefault, psName, psMinDepth, psMaxDepth);
  29. (* List class for object picking.
  30. This list is used to store the results of a PickObjects call. *)
  31. TGLPickList = class(TGLPersistentObjectList)
  32. private
  33. function GetFar(aValue: Integer): Single;
  34. function GetHit(aValue: Integer): TObject;
  35. function GetNear(aValue: Integer): Single;
  36. function GetSubObjects(aValue: Integer): TPickSubObjects;
  37. public
  38. constructor Create(aSortType: TPickSortType); reintroduce;
  39. procedure AddHit(obj: TObject; const subObj: TPickSubObjects; 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. TGLBaseSelectTechnique = class
  48. protected
  49. FObjectStack: array of TObject;
  50. FNameStack: array[0..255] of Cardinal;
  51. FCurrentName: Integer;
  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: TGLPickList); 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. TGLBaseSelectTechniqueClass = class of TGLBaseSelectTechnique;
  74. TGLSelectRenderModeTechnique = class(TGLBaseSelectTechnique)
  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: TGLPickList); override;
  88. property ObjectCountGuess;
  89. property Hits;
  90. property CurrentObject;
  91. end;
  92. function GetBestSelectorClass: TGLBaseSelectTechniqueClass; inline;
  93. //------------------------------------------------------------
  94. implementation
  95. //------------------------------------------------------------
  96. function GetBestSelectorClass: TGLBaseSelectTechniqueClass;
  97. begin
  98. // if TGLSelectRenderToTextureTechnique.IsSupported then
  99. // Result := TGLSelectRenderToTextureTechnique
  100. // else
  101. Result := TGLSelectRenderModeTechnique;
  102. end;
  103. // ------------------
  104. // ------------------ TGLPickList ------------------
  105. // ------------------
  106. var
  107. vPickListSortFlag: TPickSortType;
  108. constructor TGLPickList.Create(aSortType: TPickSortType);
  109. begin
  110. vPickListSortFlag := aSortType;
  111. inherited Create;
  112. end;
  113. function Comparefunction(item1, item2: TObject): Integer;
  114. var
  115. diff: Single;
  116. begin
  117. Result := 0;
  118. case vPickListSortFlag of
  119. psName:
  120. Result := CompareText(TComponent(TPickRecord(Item1).AObject).Name,
  121. TComponent(TPickRecord(Item1).AObject).Name);
  122. psMinDepth:
  123. begin
  124. Diff := TPickRecord(Item1).ZMin - TPickRecord(Item2).ZMin;
  125. if Diff < 0 then
  126. Result := -1
  127. else if Diff > 0 then
  128. Result := 1
  129. else
  130. Result := 0;
  131. end;
  132. psMaxDepth:
  133. begin
  134. Diff := TPickRecord(Item1).ZMax - TPickRecord(Item2).ZMax;
  135. if Diff < 0 then
  136. Result := -1
  137. else if Diff > 0 then
  138. Result := 1
  139. else
  140. Result := 0;
  141. end;
  142. end;
  143. end;
  144. procedure TGLPickList.AddHit(obj: TObject;
  145. const subObj: TPickSubObjects; zMin, zMax: Single);
  146. var
  147. newRecord: TPickRecord;
  148. begin
  149. newRecord := TPickRecord.Create;
  150. newRecord.AObject := TGLUpdateAbleComponent(obj);
  151. newRecord.SubObjects := subObj;
  152. newRecord.zMin := zMin;
  153. newRecord.zMax := zMax;
  154. Add(newRecord);
  155. if vPickListSortFlag <> psDefault then
  156. Sort(@Comparefunction);
  157. end;
  158. procedure TGLPickList.Clear;
  159. begin
  160. DoClean;
  161. inherited;
  162. end;
  163. function TGLPickList.FindObject(aObject: TObject): Integer;
  164. var
  165. i: Integer;
  166. begin
  167. Result := -1;
  168. if Assigned(AObject) then
  169. for i := 0 to Count - 1 do
  170. begin
  171. if Hit[i] = AObject then
  172. begin
  173. Result := i;
  174. Break;
  175. end;
  176. end;
  177. end;
  178. function TGLPickList.GetFar(aValue: Integer): Single;
  179. begin
  180. Result := TPickRecord(Items[AValue]).ZMax;
  181. end;
  182. function TGLPickList.GetHit(aValue: Integer): TObject;
  183. begin
  184. Result := TPickRecord(Items[AValue]).AObject;
  185. end;
  186. function TGLPickList.GetNear(aValue: Integer): Single;
  187. begin
  188. Result := TPickRecord(Items[AValue]).ZMin;
  189. end;
  190. function TGLPickList.GetSubObjects(aValue: Integer): TPickSubobjects;
  191. begin
  192. Result := TPickRecord(Items[AValue]).SubObjects;
  193. end;
  194. // ------------------
  195. // ------------------ TGLSelectRenderModeTechnique ------------------
  196. // ------------------
  197. function TGLSelectRenderModeTechnique.GetHits: Integer;
  198. begin
  199. Result := FHits;
  200. end;
  201. procedure TGLSelectRenderModeTechnique.SetHits(Value: Integer);
  202. begin
  203. FHits := Value;
  204. end;
  205. procedure TGLSelectRenderModeTechnique.SetObjectCountGuess(Value: Integer);
  206. begin
  207. if Value<8 then
  208. Value := 8;
  209. FObjectCountGuess := Value;
  210. end;
  211. class function TGLSelectRenderModeTechnique.IsSupported: Boolean;
  212. begin
  213. Result := gl.VERSION_1_1;
  214. end;
  215. procedure TGLSelectRenderModeTechnique.Start;
  216. begin
  217. SetLength(FBuffer, FObjectCountGuess * 4 + 32);
  218. gl.SelectBuffer(FObjectCountGuess * SizeOf(Cardinal), @FBuffer[0]);
  219. gl.RenderMode(GL_SELECT);
  220. gl.InitNames;
  221. FCurrentName := 0;
  222. SetLength(FObjectStack, MAX_OBJECT_STACK_DEPTH);
  223. FStackPosition := 0;
  224. gl.PushName(0);
  225. end;
  226. function TGLSelectRenderModeTechnique.Stop: Boolean;
  227. begin
  228. gl.Flush;
  229. FHits := gl.RenderMode(GL_RENDER);
  230. Result := FHits > -1;
  231. if not Result then
  232. Inc(FObjectCountGuess);
  233. end;
  234. procedure TGLSelectRenderModeTechnique.FillPickingList(var AList: TGLPickList);
  235. var
  236. subObj: TPickSubObjects;
  237. next, current, subObjIndex: Cardinal;
  238. szmin, szmax: Single;
  239. I: Integer;
  240. begin
  241. if not Assigned(AList) then
  242. AList := TGLPickList.Create(psDefault)
  243. else
  244. AList.Clear;
  245. if FHits > -1 then
  246. begin
  247. next := 0;
  248. for I := 0 to FHits - 1 do
  249. begin
  250. current := next;
  251. next := current + FBuffer[current] + 3;
  252. szmin := (FBuffer[current + 1] shr 1) * (1 / MaxInt);
  253. szmax := (FBuffer[current + 2] shr 1) * (1 / MaxInt);
  254. subObj := nil;
  255. subObjIndex := current + 4;
  256. if subObjIndex < next then
  257. begin
  258. SetLength(subObj, FBuffer[current] - 1);
  259. while subObjIndex < next do
  260. begin
  261. subObj[subObjIndex - current - 4] := FBuffer[subObjIndex];
  262. inc(subObjIndex);
  263. end;
  264. end;
  265. AList.AddHit(FObjectStack[FBuffer[current + 3]], subObj, szmin, szmax);
  266. end;
  267. end;
  268. // Restore initial object stack length
  269. SetLength(FObjectStack, MAX_OBJECT_STACK_DEPTH);
  270. end;
  271. function TGLSelectRenderModeTechnique.GetObject: TObject;
  272. begin
  273. Result := FObjectStack[FCurrentName];
  274. end;
  275. procedure TGLSelectRenderModeTechnique.SetObject(Value: TObject);
  276. begin
  277. // Grow Object stack length if needed
  278. if FCurrentName >= Length(FObjectStack) then
  279. SetLength(FObjectStack, Length(FObjectStack) * 2);
  280. FObjectStack[FCurrentName] := Value;
  281. gl.LoadName(FCurrentName);
  282. Inc(FCurrentName);
  283. end;
  284. end.