GLS.MultiPolygon.pas 21 KB

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