GXS.MultiPolygon.pas 21 KB

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