2
0

GLS.MultiPolygon.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.MultiPolygon;
  5. (* Object with support for complex polygons.
  6. When the tesselator finds an intersection of edges it wants us to give him some storage
  7. for this new vertex, and he wants a pointer (see tessCombine). The pointers taken from
  8. TGLAffineVectorList become invalid after enlarging the capacity (makes a ReAllocMem), which
  9. can happen implicitly while adding. The TGLVectorPool keeps all pointers valid until the
  10. destruction itself.
  11. Reactivated the TGLVectorPool object. The GLS.VectorLists are not suitable for this job.
  12. If anyone feels responsible: it would be fine to have a method ImportFromFile (dxf?) in
  13. the TGLContour and TGLMultiPolygonBase objects...
  14. *)
  15. interface
  16. {$I GLScene.inc}
  17. uses
  18. Winapi.OpenGL,
  19. Winapi.OpenGLext,
  20. System.Classes,
  21. System.SysUtils,
  22. GLS.OpenGLTokens,
  23. GLS.OpenGLAdapter,
  24. GLS.Spline,
  25. GLS.XOpenGL,
  26. GLS.Context,
  27. GLS.VectorTypes,
  28. GLS.VectorGeometry,
  29. GLS.VectorLists,
  30. GLS.PersistentClasses,
  31. GLS.Scene,
  32. GLS.Objects,
  33. GLS.GeomObjects,
  34. GLS.Nodes,
  35. GLS.BaseClasses,
  36. GLS.Coordinates,
  37. GLS.RenderContextInfo;
  38. type
  39. TGLContourNodes = class(TGLNodes)
  40. public
  41. procedure NotifyChange; override;
  42. end;
  43. TGLContour = class(TCollectionItem)
  44. private
  45. FNodes: TGLContourNodes;
  46. FDivision: Integer;
  47. FSplineMode: TGLLineSplineMode;
  48. FDescription: string;
  49. procedure SetNodes(const Value: TGLContourNodes);
  50. procedure SetDivision(Value: Integer);
  51. procedure SetSplineMode(const Value: TGLLineSplineMode);
  52. procedure SetDescription(const Value: string);
  53. protected
  54. procedure CreateNodes;
  55. procedure NodesChanged(Sender: TObject);
  56. function GetDisplayName: string; override;
  57. public
  58. constructor Create(Collection: TCollection); override;
  59. destructor Destroy; override;
  60. procedure Assign(Source: TPersistent); override;
  61. published
  62. property Description: string read FDescription write SetDescription;
  63. // The nodes list
  64. property Nodes: TGLContourNodes read FNodes write SetNodes;
  65. (* Number of divisions for each segment in spline modes.
  66. Minimum 1 (disabled), ignored in lsmLines mode. *)
  67. property Division: Integer read FDivision write SetDivision default 10;
  68. (* Default spline drawing mode.
  69. This mode is used only for the curve, not for the rotation path. *)
  70. property SplineMode: TGLLineSplineMode read FSplineMode write SetSplineMode default lsmLines;
  71. end;
  72. TGLContourClass = class of TGLContour;
  73. TGLContours = class(TGLNotifyCollection)
  74. private
  75. function GetItems(index: Integer): TGLContour;
  76. procedure SetItems(index: Integer; const Value: TGLContour);
  77. protected
  78. public
  79. constructor Create(AOwner: TComponent); overload;
  80. function Add: TGLContour; inline;
  81. function FindItemID(ID: Integer): TGLContour; inline;
  82. property Items[index: Integer]: TGLContour read GetItems write SetItems; default;
  83. procedure GetExtents(var min, max: TAffineVector);
  84. end;
  85. TGLPolygonList = class(TGLPersistentObjectList)
  86. private
  87. FAktList: TGLAffineVectorList;
  88. function GetList(I: Integer): TGLAffineVectorList;
  89. public
  90. procedure Add;
  91. property AktList: TGLAffineVectorList read FAktList;
  92. property List[I: Integer]: TGLAffineVectorList read GetList;
  93. end;
  94. (* Multipolygon is defined with multiple contours.
  95. The contours have to be in the X-Y plane, otherwise they are projected
  96. to it (this is done automatically by the tesselator).
  97. The plane normal is pointing in +Z. All contours are automatically closed,
  98. so there is no need to specify the last node equal to the first one.
  99. Contours should be defined counterclockwise, the first contour (index = 0)
  100. is taken as is, all following are reversed. This means you can define the
  101. outer contour first and the holes and cutouts after that. If you give the
  102. following contours in clockwise order, the first contour is extended.
  103. TGLMultiPolygonBase will take the input contours and let the tesselator
  104. make an outline from it (this is done in RetreiveOutline). This outline is
  105. used for Rendering. Only when there are changes in the contours, the
  106. outline will be recalculated. The ouline in fact is a list of GLS.VectorLists. *)
  107. TGLMultiPolygonBase = class(TGLSceneObject)
  108. private
  109. FContours: TGLContours;
  110. FOutline: TGLPolygonList;
  111. FContoursNormal: TAffineVector;
  112. FAxisAlignedDimensionsCache: TGLVector;
  113. procedure SetContours(const Value: TGLContours);
  114. function GetPath(i: Integer): TGLContourNodes;
  115. procedure SetPath(i: Integer; const value: TGLContourNodes);
  116. function GetOutline: TGLPolygonList;
  117. procedure SetContoursNormal(const Value: TAffineVector);
  118. protected
  119. procedure RenderTesselatedPolygon(textured: Boolean;
  120. normal: PAffineVector; invertNormals: Boolean);
  121. procedure RetrieveOutline(List: TGLPolygonList);
  122. procedure ContourChanged(Sender: TObject); virtual;
  123. //property PNormal:PAffineVector read FPNormal;
  124. public
  125. constructor Create(AOwner: TComponent); override;
  126. destructor Destroy; override;
  127. procedure Assign(Source: TPersistent); override;
  128. procedure AddNode(const i: Integer; const coords: TGLCoordinates); overload;
  129. procedure AddNode(const i: Integer; const X, Y, Z: TGLfloat); overload;
  130. procedure AddNode(const i: Integer; const value: TGLVector); overload;
  131. procedure AddNode(const i: Integer; const value: TAffineVector); overload;
  132. property Path[i: Integer]: TGLContourNodes read GetPath write SetPath;
  133. property Outline: TGLPolygonList read GetOutline;
  134. property ContoursNormal: TAffineVector read FContoursNormal write SetContoursNormal;
  135. function AxisAlignedDimensionsUnscaled: TGLVector; override;
  136. procedure StructureChanged; override;
  137. published
  138. property Contours: TGLContours read FContours write SetContours;
  139. end;
  140. (* A polygon that can have holes and multiple contours.
  141. Use the Path property to access a contour or one of the AddNode methods
  142. to add a node to a contour (contours are allocated automatically). *)
  143. TGLMultiPolygon = class(TGLMultiPolygonBase)
  144. private
  145. FParts: TGLPolygonParts;
  146. protected
  147. procedure SetParts(const value: TGLPolygonParts);
  148. public
  149. constructor Create(AOwner: TComponent); override;
  150. procedure Assign(Source: TPersistent); override;
  151. procedure BuildList(var rci: TGLRenderContextInfo); override;
  152. published
  153. property Parts: TGLPolygonParts read FParts write SetParts default [ppTop, ppBottom];
  154. end;
  155. (* Page oriented pointer array, with persistent pointer target memory.
  156. In TGLVectorList a pointer to a vector will not be valid any more after
  157. a call to SetCapacity, which might be done implicitely during Add.
  158. The TGLVectorPool keeps memory in its original position during its
  159. whole lifetime. *)
  160. TGLVectorPool = class(TList)
  161. private
  162. FEntrySize: Integer; // size of each entry
  163. FPageSize: Integer; // number of entries per page
  164. FArrSize: Integer; // size of one page
  165. FUsedEntries: Integer; // used entries in actual page
  166. FAktArray: GLS.VectorGeometry.PByteArray; // pointer to actual page
  167. procedure CreatePage; // creates new page
  168. public
  169. constructor Create(APageSize, AEntrySize: Integer);
  170. destructor Destroy; override;
  171. { retrieve pointer to new entry. will create new page if needed }
  172. procedure GetNewVector(var P: Pointer);
  173. end;
  174. //-------------------------------------------------------------
  175. implementation
  176. //-------------------------------------------------------------
  177. //----------------------------------------
  178. // TGLVectorPool
  179. //----------------------------------------
  180. constructor TGLVectorPool.Create(APageSize, AEntrySize: Integer);
  181. begin
  182. inherited Create;
  183. Assert(APageSize > 0);
  184. Assert(AEntrySize > 0);
  185. FPageSize := APageSize;
  186. FEntrySize := AEntrySize;
  187. FArrSize := FPageSize * FEntrySize;
  188. CreatePage;
  189. end;
  190. procedure TGLVectorPool.CreatePage;
  191. begin
  192. GetMem(FAktArray, FArrSize);
  193. Add(FAktArray);
  194. FUsedEntries := 0;
  195. end;
  196. destructor TGLVectorPool.Destroy;
  197. var
  198. i: Integer;
  199. begin
  200. for i := Count - 1 downto 0 do
  201. FreeMem(Items[i], FArrSize);
  202. inherited;
  203. end;
  204. procedure TGLVectorPool.GetNewVector(var P: Pointer);
  205. begin
  206. if FUsedEntries >= FPageSize then
  207. CreatePage;
  208. Inc(FUsedEntries);
  209. P := @(FAktArray[(FUsedEntries - 1) * FEntrySize]);
  210. end;
  211. // ------------------
  212. // ------------------ TGLPolygonList ------------------
  213. // ------------------
  214. procedure TGLPolygonList.Add;
  215. begin
  216. FAktList := TGLAffineVectorList.Create;
  217. inherited Add(FAktList);
  218. end;
  219. function TGLPolygonList.GetList(i: Integer): TGLAffineVectorList;
  220. begin
  221. Result := TGLAffineVectorList(Items[i]);
  222. end;
  223. // ------------------
  224. // ------------------ TGLContour ------------------
  225. // ------------------
  226. constructor TGLContour.Create(Collection: TCollection);
  227. begin
  228. inherited;
  229. CreateNodes;
  230. FDivision := 10;
  231. FSplineMode := lsmLines;
  232. end;
  233. procedure TGLContour.CreateNodes;
  234. begin
  235. FNodes := TGLContourNodes.Create(Self);
  236. end;
  237. destructor TGLContour.Destroy;
  238. begin
  239. FNodes.Free;
  240. inherited;
  241. end;
  242. procedure TGLContour.Assign(Source: TPersistent);
  243. begin
  244. if Source is TGLContour then
  245. begin
  246. FNodes.Assign(TGLContour(Source).FNodes);
  247. FDivision := TGLContour(Source).FDivision;
  248. FSplineMode := TGLContour(Source).FSplineMode;
  249. FDescription := TGLContour(Source).FDescription;
  250. end
  251. else
  252. inherited;
  253. end;
  254. function TGLContour.GetDisplayName: string;
  255. begin
  256. result := Description;
  257. if result = '' then
  258. result := Format('GLContour: %d nodes', [Nodes.Count]);
  259. end;
  260. procedure TGLContour.NodesChanged(Sender: TObject);
  261. begin
  262. Changed(false);
  263. end;
  264. procedure TGLContour.SetDescription(const Value: string);
  265. begin
  266. FDescription := Value;
  267. end;
  268. procedure TGLContour.SetDivision(Value: Integer);
  269. begin
  270. if Value < 1 then
  271. Value := 1;
  272. if Value <> FDivision then
  273. begin
  274. FDivision := value;
  275. Changed(false);
  276. end;
  277. end;
  278. procedure TGLContour.SetNodes(const Value: TGLContourNodes);
  279. begin
  280. FNodes.Assign(Value);
  281. Changed(false);
  282. end;
  283. procedure TGLContour.SetSplineMode(const Value: TGLLineSplineMode);
  284. begin
  285. if FSplineMode <> value then
  286. begin
  287. FSplineMode := value;
  288. Changed(false);
  289. end;
  290. end;
  291. //-----------------------------
  292. // TGLContours
  293. //-----------------------------
  294. function TGLContours.Add: TGLContour;
  295. begin
  296. Result := TGLContour(inherited Add);
  297. end;
  298. constructor TGLContours.Create(AOwner: TComponent);
  299. begin
  300. Create(AOwner, TGLContour);
  301. end;
  302. function TGLContours.FindItemID(ID: Integer): TGLContour;
  303. begin
  304. result := TGLContour(inherited FindItemId(Id));
  305. end;
  306. function TGLContours.GetItems(index: Integer): TGLContour;
  307. begin
  308. result := TGLContour(inherited Items[index]);
  309. end;
  310. procedure TGLContours.SetItems(index: Integer; const Value: TGLContour);
  311. begin
  312. inherited Items[index] := value;
  313. end;
  314. procedure TGLContours.GetExtents(var min, max: TAffineVector);
  315. var
  316. i, k: Integer;
  317. lMin, lMax: TAffineVector;
  318. const
  319. cBigValue: Single = 1e30;
  320. cSmallValue: Single = -1e30;
  321. begin
  322. SetVector(min, cBigValue, cBigValue, cBigValue);
  323. SetVector(max, cSmallValue, cSmallValue, cSmallValue);
  324. for i := 0 to Count - 1 do
  325. begin
  326. GetItems(i).Nodes.GetExtents(lMin, lMax);
  327. for k := 0 to 2 do
  328. begin
  329. if lMin.V[k] < min.V[k] then min.V[k] := lMin.V[k];
  330. if lMax.V[k] > max.V[k] then max.V[k] := lMax.V[k];
  331. end;
  332. end;
  333. end;
  334. //---------------------------------
  335. // TGLMultiPolygonBase
  336. //---------------------------------
  337. constructor TGLMultiPolygonBase.Create(AOwner: TComponent);
  338. begin
  339. inherited;
  340. FContours := TGLContours.Create(Self);
  341. FContours.OnNotifyChange := ContourChanged;
  342. FContoursNormal := AffineVectorMake(0, 0, 1);
  343. FAxisAlignedDimensionsCache.X := -1;
  344. end;
  345. destructor TGLMultiPolygonBase.Destroy;
  346. begin
  347. if FOutline <> nil then
  348. begin
  349. FOutline.Clean;
  350. FreeAndNil(FOutline);
  351. end;
  352. FContours.Free;
  353. inherited;
  354. end;
  355. procedure TGLMultiPolygonBase.Assign(Source: TPersistent);
  356. begin
  357. if Source is TGLMultiPolygonBase then
  358. begin
  359. FContours.Assign(TGLMultiPolygonBase(Source).FContours);
  360. end;
  361. inherited;
  362. end;
  363. procedure TGLMultiPolygonBase.ContourChanged(Sender: TObject);
  364. begin
  365. if Assigned(FOutline) then
  366. begin
  367. // force a RetrieveOutline with next Render
  368. FOutline.Clean;
  369. FreeAndNil(FOutline);
  370. StructureChanged;
  371. end;
  372. end;
  373. procedure TGLMultiPolygonBase.AddNode(const i: Integer; const value: TGLVector);
  374. begin
  375. Path[i].AddNode(value);
  376. end;
  377. procedure TGLMultiPolygonBase.AddNode(const i: Integer; const x, y, z: TGLfloat);
  378. begin
  379. Path[i].AddNode(x, y, z);
  380. end;
  381. procedure TGLMultiPolygonBase.AddNode(const i: Integer; const coords: TGLCoordinates);
  382. begin
  383. Path[i].AddNode(coords);
  384. end;
  385. procedure TGLMultiPolygonBase.AddNode(const I: Integer; const value: TAffineVector);
  386. begin
  387. Path[i].AddNode(value);
  388. end;
  389. procedure TGLMultiPolygonBase.SetContours(const Value: TGLContours);
  390. begin
  391. FContours.Assign(Value);
  392. end;
  393. function TGLMultiPolygonBase.GetOutline: TGLPolygonList;
  394. begin
  395. if not Assigned(FOutline) then
  396. begin
  397. FOutline := TGLPolygonList.Create;
  398. RetrieveOutline(FOutline);
  399. end;
  400. Result := FOutline;
  401. end;
  402. function TGLMultiPolygonBase.GetPath(i: Integer): TGLContourNodes;
  403. begin
  404. Assert(i >= 0);
  405. while i >= Contours.Count do
  406. Contours.Add;
  407. Result := Contours[i].Nodes;
  408. end;
  409. procedure TGLMultiPolygonBase.SetPath(i: Integer; const value: TGLContourNodes);
  410. begin
  411. Assert(i >= 0);
  412. while i >= Contours.Count do
  413. Contours.Add;
  414. Contours[i].Nodes.Assign(value);
  415. end;
  416. //
  417. // Tessellation routines (OpenGL callbacks)
  418. //
  419. var
  420. vVertexPool: TGLVectorPool;
  421. procedure tessError(errno: Cardinal);
  422. {$IFDEF MSWINDOWS} stdcall;{$ELSE}cdecl;{$ENDIF}
  423. begin
  424. Assert(False, IntToStr(errno) + ' : ' + string(gluErrorString(errno)));
  425. end;
  426. procedure tessIssueVertex(vertexData: Pointer);
  427. {$IFDEF MSWINDOWS} stdcall;{$ELSE}cdecl;{$ENDIF}
  428. begin
  429. xgl.TexCoord2fv(vertexData);
  430. gl.Vertex3fv(vertexData);
  431. end;
  432. procedure tessCombine(coords: PDoubleVector; vertex_data: Pointer;
  433. weight: PGLFloat; var outData: Pointer);
  434. {$IFDEF MSWINDOWS} stdcall;{$ELSE}cdecl;{$ENDIF}
  435. begin
  436. vVertexPool.GetNewVector(outData);
  437. SetVector(PAffineVector(outData)^, coords^[0], coords^[1], coords^[2]);
  438. end;
  439. procedure tessBeginList(typ: Cardinal; polygonData: Pointer);
  440. {$IFDEF MSWINDOWS} stdcall;{$ELSE}cdecl;{$ENDIF}
  441. begin
  442. TGLPolygonList(polygonData).Add;
  443. end;
  444. procedure tessIssueVertexList(vertexData: Pointer; polygonData: Pointer);
  445. {$IFDEF MSWINDOWS} stdcall;{$ELSE}cdecl;{$ENDIF}
  446. begin
  447. TGLPolygonList(polygonData).AktList.Add(PAffineVector(vertexData)^);
  448. end;
  449. procedure TGLMultiPolygonBase.RetrieveOutline(List: TGLPolygonList);
  450. var
  451. i, n: Integer;
  452. tess: PGLUTesselator;
  453. procedure TesselatePath(contour: TGLContour; inverted: Boolean);
  454. procedure IssueVertex(v: TAffineVector);
  455. var
  456. dblVector: TAffineDblVector;
  457. p: PAffineVector;
  458. begin
  459. vVertexPool.GetNewVector(Pointer(p));
  460. p^ := v;
  461. SetVector(dblVector, v);
  462. gluTessVertex(tess, dblVector, p);
  463. end;
  464. var
  465. i, n: Integer;
  466. spline: TCubicSpline;
  467. f: Single;
  468. splineDivisions: Integer;
  469. nodes: TGLContourNodes;
  470. begin
  471. gluTessBeginContour(tess);
  472. nodes := contour.Nodes;
  473. if contour.SplineMode = lsmLines then
  474. splineDivisions := 0
  475. else
  476. splineDivisions := contour.Division;
  477. if splineDivisions > 1 then
  478. begin
  479. spline := nodes.CreateNewCubicSpline;
  480. try
  481. f := 1 / splineDivisions;
  482. n := splineDivisions * (nodes.Count - 1);
  483. if inverted then
  484. begin
  485. for i := n downto 0 do
  486. IssueVertex(spline.SplineAffineVector(i * f))
  487. end
  488. else
  489. begin
  490. for i := 0 to n do
  491. IssueVertex(spline.SplineAffineVector(i * f));
  492. end;
  493. finally
  494. spline.Free;
  495. end;
  496. end
  497. else
  498. begin
  499. n := nodes.Count - 1;
  500. if inverted then
  501. begin
  502. for i := n downto 0 do
  503. IssueVertex(nodes[i].AsAffineVector)
  504. end
  505. else
  506. begin
  507. for i := 0 to n do
  508. IssueVertex(nodes[i].AsAffineVector);
  509. end;
  510. end;
  511. gluTessEndContour(tess);
  512. end;
  513. begin
  514. List.Clear;
  515. if (Contours.Count > 0) and (Path[0].Count > 2) then
  516. begin
  517. // Vertex count
  518. n := 0;
  519. for i := 0 to Contours.Count - 1 do
  520. n := n + Path[i].Count;
  521. // Creates and initialize the GLU tesselator
  522. vVertexPool := TGLVectorPool.Create(n, SizeOf(TAffineVector));
  523. tess := gluNewTess;
  524. try
  525. // register callbacks
  526. gluTessCallback(tess, GLU_TESS_BEGIN_DATA, @tessBeginList);
  527. gluTessCallback(tess, GLU_TESS_END_DATA, nil);
  528. gluTessCallback(tess, GLU_TESS_VERTEX_DATA, @tessIssueVertexList);
  529. gluTessCallback(tess, GLU_TESS_ERROR, @tessError);
  530. gluTessCallback(tess, GLU_TESS_COMBINE, @tessCombine);
  531. // issue normal
  532. gluTessNormal(tess, FContoursNormal.X, FContoursNormal.Y, FContoursNormal.Z);
  533. // set properties
  534. gluTessProperty(Tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
  535. gluTessProperty(Tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
  536. gluTessBeginPolygon(tess, List);
  537. // outside contour
  538. TesselatePath(Contours[0], False);
  539. // inside contours
  540. for n := 1 to Contours.Count - 1 do
  541. TesselatePath(Contours[n], True);
  542. gluTessEndPolygon(tess);
  543. finally
  544. gluDeleteTess(tess);
  545. vVertexPool.Free;
  546. vVertexPool := nil;
  547. end;
  548. end;
  549. end;
  550. procedure TGLMultiPolygonBase.RenderTesselatedPolygon(textured: Boolean;
  551. normal: PAffineVector;
  552. invertNormals: Boolean);
  553. var
  554. tess: PGLUTesselator;
  555. procedure IssueVertex(v: TAffineVector);
  556. var
  557. dblVector: TAffineDblVector;
  558. p: PAffineVector;
  559. begin
  560. vVertexPool.GetNewVector(Pointer(p));
  561. p^ := v;
  562. SetVector(dblVector, v);
  563. gluTessVertex(tess, dblVector, p);
  564. end;
  565. var
  566. i, n: Integer;
  567. begin
  568. // call to Outline will call RetrieveOutline if necessary
  569. if (Outline.Count = 0) or (Outline.List[0].Count < 2) then
  570. Exit;
  571. // Vertex count
  572. n := 0;
  573. for i := 0 to Outline.Count - 1 do
  574. n := n + Outline.List[i].Count;
  575. // Creates and initialize a vertex pool and the GLU tesselator
  576. vVertexPool := TGLVectorPool.Create(n, Sizeof(TAffineVector));
  577. tess := gluNewTess;
  578. try
  579. gluTessCallback(tess, GLU_TESS_BEGIN, @gl.Begin_);
  580. if textured then
  581. gluTessCallback(tess, GLU_TESS_VERTEX, @tessIssueVertex)
  582. else
  583. gluTessCallback(tess, GLU_TESS_VERTEX, @gl.Vertex3fv);
  584. gluTessCallback(tess, GLU_TESS_END, @gl.End_);
  585. gluTessCallback(tess, GLU_TESS_ERROR, @tessError);
  586. gluTessCallback(tess, GLU_TESS_COMBINE, @tessCombine);
  587. // Issue normal
  588. if Assigned(normal) then
  589. begin
  590. gl.Normal3fv(PGLFloat(normal));
  591. gluTessNormal(tess, normal^.X, normal^.Y, normal^.Z);
  592. end;
  593. gluTessProperty(Tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE);
  594. // Issue polygon
  595. gluTessBeginPolygon(tess, nil);
  596. for n := 0 to Outline.Count - 1 do
  597. begin
  598. with Outline.List[n] do
  599. begin
  600. gluTessBeginContour(tess);
  601. if invertNormals then
  602. for i := Count - 1 downto 0 do
  603. IssueVertex(Items[i])
  604. else
  605. for i := 0 to Count - 1 do
  606. IssueVertex(Items[i]);
  607. gluTessEndContour(tess);
  608. end;
  609. end;
  610. gluTessEndPolygon(tess);
  611. finally
  612. gluDeleteTess(tess);
  613. vVertexPool.Free;
  614. vVertexPool := nil;
  615. end;
  616. end;
  617. // ------------------
  618. // ------------------ TGLMultiPolygon ------------------
  619. // ------------------
  620. constructor TGLMultiPolygon.Create(AOwner: TComponent);
  621. begin
  622. inherited;
  623. FParts := [ppTop, ppBottom];
  624. end;
  625. procedure TGLMultiPolygon.Assign(Source: TPersistent);
  626. begin
  627. if Source is TGLMultiPolygon then
  628. begin
  629. FParts := TGLMultiPolygon(Source).FParts;
  630. end;
  631. inherited;
  632. end;
  633. procedure TGLMultiPolygon.BuildList(var rci: TGLRenderContextInfo);
  634. var
  635. normal: TAffineVector;
  636. begin
  637. if (Outline.Count < 1) then
  638. Exit;
  639. normal := ContoursNormal;
  640. // Render
  641. // tessellate top polygon
  642. if ppTop in FParts then
  643. RenderTesselatedPolygon(True, @normal, False);
  644. // tessellate bottom polygon
  645. if ppBottom in FParts then
  646. begin
  647. NegateVector(normal);
  648. RenderTesselatedPolygon(True, @normal, True)
  649. end;
  650. end;
  651. procedure TGLMultiPolygon.SetParts(const value: TGLPolygonParts);
  652. begin
  653. if FParts <> value then
  654. begin
  655. FParts := value;
  656. StructureChanged;
  657. end;
  658. end;
  659. procedure TGLMultiPolygonBase.SetContoursNormal(const Value: TAffineVector);
  660. begin
  661. FContoursNormal := Value;
  662. end;
  663. function TGLMultiPolygonBase.AxisAlignedDimensionsUnscaled: TGLVector;
  664. var
  665. dMin, dMax: TAffineVector;
  666. begin
  667. if FAxisAlignedDimensionsCache.X < 0 then
  668. begin
  669. Contours.GetExtents(dMin, dMax);
  670. FAxisAlignedDimensionsCache.X := MaxFloat(Abs(dMin.X), Abs(dMax.X));
  671. FAxisAlignedDimensionsCache.Y := MaxFloat(Abs(dMin.Y), Abs(dMax.Y));
  672. FAxisAlignedDimensionsCache.Z := MaxFloat(Abs(dMin.Z), Abs(dMax.Z));
  673. end;
  674. SetVector(Result, FAxisAlignedDimensionsCache);
  675. end;
  676. procedure TGLMultiPolygonBase.StructureChanged;
  677. begin
  678. FAxisAlignedDimensionsCache.X := -1;
  679. inherited;
  680. end;
  681. // ------------------
  682. // ------------------ TGLContourNodes ------------------
  683. // ------------------
  684. procedure TGLContourNodes.NotifyChange;
  685. begin
  686. if (GetOwner <> nil) then
  687. (GetOwner as TGLContour).Changed(False);
  688. end;
  689. //-------------------------------------------------------------
  690. initialization
  691. //-------------------------------------------------------------
  692. RegisterClass(TGLMultiPolygon);
  693. end.