GLS.Nodes.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.Nodes;
  5. (* Nodes are used to describe lines, polygons + more. *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. Stage.OpenGLTokens,
  12. Stage.VectorTypes,
  13. Stage.VectorGeometry,
  14. GLS.OpenGLAdapter,
  15. GLS.Context,
  16. GLS.BaseClasses,
  17. Stage.Spline,
  18. GLS.Coordinates;
  19. type
  20. TGLNode = class(TCollectionItem)
  21. private
  22. FCoords: TGLVector;
  23. FTagObject: TObject;
  24. procedure SetAsVector(const Value: TGLVector);
  25. procedure SetAsAffineVector(const Value: TAffineVector);
  26. function GetAsAffineVector: TAffineVector;
  27. procedure SetCoordinate(AIndex: Integer; AValue: TGLFloat);
  28. function GetCoordinate(const Index: Integer): TGLFloat;
  29. protected
  30. function StoreCoordinate(AIndex: Integer): Boolean;
  31. function GetDisplayName: string; override;
  32. public
  33. constructor Create(ACollection: TCollection); override;
  34. destructor Destroy; override;
  35. procedure Assign(Source: TPersistent); override;
  36. function AsAddress: PGLFloat;
  37. (* The coordinates viewed as a vector.
  38. Assigning a value to this property will trigger notification events,
  39. if you don't want so, use DirectVector instead. *)
  40. property AsVector: TGLVector read FCoords write SetAsVector;
  41. (* The coordinates viewed as an affine vector.
  42. Assigning a value to this property will trigger notification events,
  43. if you don't want so, use DirectVector instead.
  44. The W component is automatically adjustes depending on style. *)
  45. property AsAffineVector: TAffineVector read GetAsAffineVector write SetAsAffineVector;
  46. property W: TGLFloat index 3 read GetCoordinate write SetCoordinate stored StoreCoordinate;
  47. property TagObject: TObject read FTagObject write FTagObject;
  48. published
  49. property X: TGLFloat index 0 read GetCoordinate write SetCoordinate stored StoreCoordinate;
  50. property Y: TGLFloat index 1 read GetCoordinate write SetCoordinate stored StoreCoordinate;
  51. property Z: TGLFloat index 2 read GetCoordinate write SetCoordinate stored StoreCoordinate;
  52. end;
  53. TGLNodes = class(TOwnedCollection)
  54. protected
  55. procedure SetItems(Index: Integer; const Val: TGLNode);
  56. function GetItems(Index: Integer): TGLNode;
  57. procedure Update(Item: TCollectionItem); override;
  58. public
  59. constructor Create(AOwner: TPersistent; AItemClass: TCollectionItemClass = nil);
  60. function CreateCopy(AOwner: TPersistent): TGLNodes;
  61. function Add: TGLNode;
  62. function FindItemID(ID: Integer): TGLNode;
  63. property Items[index: Integer]: TGLNode read GetItems write SetItems; default;
  64. function First: TGLNode;
  65. function Last: TGLNode;
  66. procedure NotifyChange; virtual;
  67. procedure EndUpdate; override;
  68. // AddNode (TGLCustomCoordinates)
  69. procedure AddNode(const Coords: TGLCustomCoordinates); overload;
  70. procedure AddNode(const X, Y, Z: TGLfloat); overload;
  71. procedure AddNode(const Value: TGLVector); overload;
  72. procedure AddNode(const Value: TAffineVector); overload;
  73. procedure AddXYArc(XRadius, YRadius: Single; StartAngle, StopAngle: Single; NbSegments: Integer;
  74. const Center: TAffineVector);
  75. // Calculates and returns the barycenter of the nodes
  76. function Barycenter: TAffineVector;
  77. (* Computes normal based on the 1st three nodes.
  78. Returns NullVector if there are less than 3 nodes. *)
  79. function Normal: TAffineVector;
  80. // Returns normalized vector Nodes[i+1]-Nodes[i]
  81. function Vector(I: Integer): TAffineVector;
  82. (* Calculates the extents of the nodes (min-max for all coordinates).
  83. The returned values are also the two corners of the axis-aligned bounding box. *)
  84. procedure GetExtents(var Min, Max: TAffineVector);
  85. // Translate all nodes
  86. procedure Translate(const Tv: TAffineVector);
  87. // Scale all node coordinates
  88. procedure Scale(const Fv: TAffineVector); overload;
  89. // Scale all node coordinates
  90. procedure Scale(F: Single); overload;
  91. // Rotate nodes around Y axis by the given angle (degrees)
  92. procedure RotateAroundX(Angle: Single);
  93. // Rotate nodes around Y axis by the given angle (degrees)
  94. procedure RotateAroundY(Angle: Single);
  95. // Rotate nodes around Y axis by the given angle (degrees)
  96. procedure RotateAroundZ(Angle: Single);
  97. procedure RenderTesselatedPolygon(ATextured: Boolean; ANormal: PAffineVector = nil; ASplineDivisions: Integer = 1;
  98. AInvertNormals: Boolean = False);
  99. function CreateNewCubicSpline: TCubicSpline;
  100. end;
  101. TGLNodesClass = class of TGLNodes;
  102. // --------------------------------------------------------------
  103. implementation
  104. //---------------------------------------------------------------
  105. uses
  106. SysUtils,
  107. GLS.XOpenGL;
  108. // ------------------
  109. // ------------------ TGLNode ------------------
  110. // ------------------
  111. constructor TGLNode.Create(ACollection: TCollection);
  112. begin
  113. inherited Create(ACollection);
  114. // nothing, yet
  115. end;
  116. destructor TGLNode.Destroy;
  117. begin
  118. // nothing, yet
  119. inherited Destroy;
  120. end;
  121. procedure TGLNode.Assign(Source: TPersistent);
  122. begin
  123. if Source is TGLNode then
  124. begin
  125. FCoords := TGLNode(Source).FCoords;
  126. end
  127. else
  128. inherited;
  129. end;
  130. function TGLNode.GetDisplayName: string;
  131. begin
  132. Result := Format('%.4f; %.4f; %.4f', [X, Y, Z]);
  133. end;
  134. function TGLNode.AsAddress: PGLFloat;
  135. begin
  136. Result := @FCoords;
  137. end;
  138. procedure TGLNode.SetAsVector(const Value: TGLVector);
  139. begin
  140. FCoords := Value;
  141. (Collection as TGLNodes).NotifyChange;
  142. end;
  143. procedure TGLNode.SetAsAffineVector(const Value: TAffineVector);
  144. begin
  145. SetVector(FCoords, Value);
  146. (Collection as TGLNodes).NotifyChange;
  147. end;
  148. function TGLNode.GetAsAffineVector: TAffineVector;
  149. begin
  150. SetVector(Result, FCoords);
  151. end;
  152. function TGLNode.GetCoordinate(const Index: Integer): TGLFloat;
  153. begin
  154. Result := FCoords.V[Index];
  155. end;
  156. procedure TGLNode.SetCoordinate(AIndex: Integer; AValue: TGLFloat);
  157. begin
  158. FCoords.V[AIndex] := AValue;
  159. (Collection as TGLNodes).NotifyChange;
  160. end;
  161. function TGLNode.StoreCoordinate(AIndex: Integer): Boolean;
  162. begin
  163. Result := (FCoords.V[AIndex] <> 0);
  164. end;
  165. // ------------------
  166. // ------------------ TGLNodes ------------------
  167. // ------------------
  168. constructor TGLNodes.Create(AOwner: TPersistent; AItemClass: TCollectionItemClass = nil);
  169. begin
  170. if not Assigned(AItemClass) then
  171. inherited Create(AOwner, TGLNode)
  172. else
  173. inherited Create(AOwner, AItemClass);
  174. end;
  175. function TGLNodes.CreateCopy(AOwner: TPersistent): TGLNodes;
  176. begin
  177. if Self <> nil then
  178. begin
  179. Result := TGLNodesClass(Self.ClassType).Create(AOwner);
  180. Result.Assign(Self);
  181. end
  182. else
  183. Result := nil;
  184. end;
  185. procedure TGLNodes.SetItems(Index: Integer; const Val: TGLNode);
  186. begin
  187. inherited Items[index] := Val;
  188. end;
  189. function TGLNodes.GetItems(Index: Integer): TGLNode;
  190. begin
  191. Result := TGLNode(inherited Items[index]);
  192. end;
  193. function TGLNodes.First: TGLNode;
  194. begin
  195. if Count > 0 then
  196. Result := TGLNode(inherited Items[0])
  197. else
  198. Result := nil;
  199. end;
  200. function TGLNodes.Last: TGLNode;
  201. var
  202. N: Integer;
  203. begin
  204. N := Count - 1;
  205. if N >= 0 then
  206. Result := TGLNode(inherited Items[N])
  207. else
  208. Result := nil;
  209. end;
  210. procedure TGLNodes.Update(Item: TCollectionItem);
  211. begin
  212. inherited;
  213. NotifyChange;
  214. end;
  215. function TGLNodes.Add: TGLNode;
  216. begin
  217. Result := (inherited Add) as TGLNode;
  218. end;
  219. function TGLNodes.FindItemID(ID: Integer): TGLNode;
  220. begin
  221. Result := (inherited FindItemID(ID)) as TGLNode;
  222. end;
  223. procedure TGLNodes.NotifyChange;
  224. begin
  225. if (UpdateCount = 0) and (GetOwner <> nil) and (GetOwner is TGLUpdateAbleComponent) then
  226. TGLUpdateAbleComponent(GetOwner).NotifyChange(Self);
  227. end;
  228. procedure TGLNodes.EndUpdate;
  229. begin
  230. inherited EndUpdate;
  231. // Workaround for a bug in VCL's EndUpdate
  232. if UpdateCount = 0 then
  233. NotifyChange;
  234. end;
  235. procedure TGLNodes.AddNode(const Coords: TGLCustomCoordinates);
  236. begin
  237. Add.AsVector := Coords.AsVector;
  238. end;
  239. procedure TGLNodes.AddNode(const X, Y, Z: Single);
  240. begin
  241. Add.AsVector := PointMake(X, Y, Z);
  242. end;
  243. procedure TGLNodes.AddNode(const Value: TGLVector);
  244. begin
  245. Add.AsVector := Value;
  246. end;
  247. procedure TGLNodes.AddNode(const Value: TAffineVector);
  248. begin
  249. Add.AsAffineVector := Value;
  250. end;
  251. procedure TGLNodes.AddXYArc(XRadius, YRadius: Single; StartAngle, StopAngle: Single; NbSegments: Integer;
  252. const Center: TAffineVector);
  253. var
  254. I: Integer;
  255. F: Single;
  256. S, C: Single;
  257. begin
  258. BeginUpdate;
  259. try
  260. StartAngle := DegToRadian(StartAngle);
  261. StopAngle := DegToRadian(StopAngle);
  262. F := (StopAngle - StartAngle) / NbSegments;
  263. for I := 0 to NbSegments do
  264. begin
  265. SinCosine(I * F + StartAngle, S, C);
  266. SetVector(Add.FCoords, Center.X + XRadius * C, Center.Y + YRadius * S, Center.Z, 1);
  267. end;
  268. finally
  269. EndUpdate;
  270. end;
  271. end;
  272. function TGLNodes.Barycenter: TAffineVector;
  273. var
  274. I: Integer;
  275. begin
  276. Result := NullVector;
  277. if Count > 0 then
  278. begin
  279. for I := 0 to Count - 1 do
  280. AddVector(Result, PAffineVector(Items[I].AsAddress)^);
  281. ScaleVector(Result, 1.0 / Count);
  282. end;
  283. end;
  284. function TGLNodes.Normal: TAffineVector;
  285. begin
  286. if Count >= 3 then
  287. CalcPlaneNormal(Items[0].FCoords, Items[1].FCoords, Items[2].FCoords, Result)
  288. else
  289. Result := NullVector;
  290. end;
  291. function TGLNodes.Vector(I: Integer): TAffineVector;
  292. procedure CalcUsingPrev; forward;
  293. procedure CalcUsingNext;
  294. begin
  295. if I < Count - 1 then
  296. VectorSubtract(Items[I].AsVector, Items[I + 1].AsVector, Result)
  297. else
  298. CalcUsingPrev;
  299. end;
  300. procedure CalcUsingPrev;
  301. begin
  302. if I > 0 then
  303. VectorSubtract(Items[I - 1].AsVector, Items[I].AsVector, Result)
  304. else
  305. CalcUsingNext;
  306. end;
  307. var
  308. J: Integer;
  309. Vecnull: Boolean;
  310. begin
  311. Assert((I >= 0) and (I < Count));
  312. if I = 0 then
  313. if I = Count - 1 then
  314. SetVector(Result, NullVector)
  315. else
  316. VectorSubtract(Items[I + 1].AsVector, Items[I].AsVector, Result)
  317. else if I = Count - 1 then
  318. VectorSubtract(Items[I].AsVector, Items[I - 1].AsVector, Result)
  319. else
  320. VectorSubtract(Items[I + 1].AsVector, Items[I - 1].AsVector, Result);
  321. if VectorNorm(Result) < 1E-5 then
  322. begin
  323. // avoid returning null vector which generates display bugs in geometry
  324. J := 1;
  325. Vecnull := True;
  326. while (I + J < Count) and (Vecnull) do
  327. begin
  328. VectorSubtract(Items[I + J].AsVector, Items[I].AsVector, Result);
  329. if (VectorNorm(Result) > 1E-5) then
  330. Vecnull := False
  331. else
  332. Inc(J);
  333. end;
  334. J := 1;
  335. while (I - J > 0) and (Vecnull) do
  336. begin
  337. VectorSubtract(Items[I].AsVector, Items[I - J].AsVector, Result);
  338. if (VectorNorm(Result) > 1E-5) then
  339. Vecnull := False
  340. else
  341. Inc(J);
  342. end;
  343. if Vecnull then
  344. SetVector(Result, NullVector)
  345. else
  346. NormalizeVector(Result);
  347. end
  348. else
  349. NormalizeVector(Result);
  350. end;
  351. procedure TGLNodes.GetExtents(var Min, Max: TAffineVector);
  352. var
  353. I, K: Integer;
  354. F: Single;
  355. const
  356. CBigValue: Single = 1E50;
  357. CSmallValue: Single = -1E50;
  358. begin
  359. SetVector(Min, CBigValue, CBigValue, CBigValue);
  360. SetVector(Max, CSmallValue, CSmallValue, CSmallValue);
  361. for I := 0 to Count - 1 do
  362. begin
  363. for K := 0 to 2 do
  364. begin
  365. F := PAffineVector(Items[I].AsAddress)^.V[K];
  366. if F < Min.V[K] then
  367. Min.V[K] := F;
  368. if F > Max.V[K] then
  369. Max.V[K] := F;
  370. end;
  371. end;
  372. end;
  373. procedure TGLNodes.Translate(const Tv: TAffineVector);
  374. var
  375. I: Integer;
  376. begin
  377. for I := 0 to Count - 1 do
  378. AddVector(PAffineVector(Items[I].AsAddress)^, Tv);
  379. NotifyChange;
  380. end;
  381. procedure TGLNodes.Scale(const Fv: TAffineVector);
  382. var
  383. I: Integer;
  384. begin
  385. for I := 0 to Count - 1 do
  386. ScaleVector(PAffineVector(Items[I].AsAddress)^, Fv);
  387. NotifyChange;
  388. end;
  389. procedure TGLNodes.Scale(F: Single);
  390. var
  391. I: Integer;
  392. begin
  393. for I := 0 to Count - 1 do
  394. ScaleVector(PAffineVector(Items[I].AsAddress)^, F);
  395. NotifyChange;
  396. end;
  397. procedure TGLNodes.RotateAroundX(Angle: Single);
  398. var
  399. I: Integer;
  400. C, S, V2: Single;
  401. V: PAffineVector;
  402. begin
  403. SinCosine(CPIDiv180 * Angle, S, C);
  404. for I := 0 to Count - 1 do
  405. begin
  406. V := PAffineVector(Items[I].AsAddress);
  407. V2 := V^.Z;
  408. V^.Y := C * V^.Y + S * V2;
  409. V^.Z := C * V2 - S * V^.Y;
  410. end;
  411. NotifyChange;
  412. end;
  413. procedure TGLNodes.RotateAroundY(Angle: Single);
  414. var
  415. I: Integer;
  416. C, S, V0: Single;
  417. V: PAffineVector;
  418. begin
  419. SinCosine(CPIDiv180 * Angle, S, C);
  420. for I := 0 to Count - 1 do
  421. begin
  422. V := PAffineVector(Items[I].AsAddress);
  423. V0 := V^.X;
  424. V^.X := C * V0 + S * V^.Z;
  425. V^.Z := C * V^.Z - S * V0;
  426. end;
  427. NotifyChange;
  428. end;
  429. procedure TGLNodes.RotateAroundZ(Angle: Single);
  430. var
  431. I: Integer;
  432. C, S, V1: Single;
  433. V: PAffineVector;
  434. begin
  435. SinCosine(CPIDiv180 * Angle, S, C);
  436. for I := 0 to Count - 1 do
  437. begin
  438. V := PAffineVector(Items[I].AsAddress);
  439. V1 := V^.Y;
  440. V^.Y := C * V1 + S * V^.X;
  441. V^.X := C * V^.X - S * V1;
  442. end;
  443. NotifyChange;
  444. end;
  445. function TGLNodes.CreateNewCubicSpline: TCubicSpline;
  446. var
  447. I: Integer;
  448. Xa, Ya, Za: PFloatArray;
  449. begin
  450. GetMem(Xa, SizeOf(TGLFloat) * Count);
  451. GetMem(Ya, SizeOf(TGLFloat) * Count);
  452. GetMem(Za, SizeOf(TGLFloat) * Count);
  453. for I := 0 to Count - 1 do
  454. with Items[I] do
  455. begin
  456. Xa^[I] := X;
  457. Ya^[I] := Y;
  458. Za^[I] := Z;
  459. end;
  460. Result := TCubicSpline.Create(Xa, Ya, Za, nil, Count);
  461. FreeMem(Xa);
  462. FreeMem(Ya);
  463. FreeMem(Za);
  464. end;
  465. // RenderTesselatedPolygon
  466. var
  467. NbExtraVertices: Integer;
  468. NewVertices: PAffineVectorArray;
  469. function AllocNewVertex: PAffineVector;
  470. begin
  471. Inc(NbExtraVertices);
  472. Result := @NewVertices[NbExtraVertices - 1];
  473. end;
  474. procedure TessError(Errno: TGLuint);
  475. {$IFDEF WINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
  476. begin
  477. Assert(False, IntToStr(Errno) + ': ' + string(GluErrorString(Errno)));
  478. end;
  479. procedure TessIssueVertex(VertexData: Pointer);
  480. {$IFDEF WINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
  481. begin
  482. xgl.TexCoord2fv(VertexData);
  483. gl.Vertex3fv(VertexData);
  484. end;
  485. procedure TessCombine(Coords: PDoubleVector; Vertex_data: Pointer; Weight: PGLFloat; var OutData: Pointer);
  486. {$IFDEF WINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
  487. begin
  488. OutData := AllocNewVertex;
  489. SetVector(PAffineVector(OutData)^, Coords^[0], Coords^[1], Coords^[2]);
  490. end;
  491. procedure TGLNodes.RenderTesselatedPolygon(ATextured: Boolean; ANormal: PAffineVector = nil; ASplineDivisions: Integer = 1;
  492. AInvertNormals: Boolean = False);
  493. var
  494. I: Integer;
  495. Tess: PGLUTesselator;
  496. DblVector: TAffineDblVector;
  497. Spline: TCubicSpline;
  498. SplinePos: PAffineVector;
  499. F: Single;
  500. begin
  501. if Count > 2 then
  502. begin
  503. // Creates and initialize the GLU tesselator
  504. Tess := gluNewTess;
  505. gluTessCallback(Tess, GLU_TESS_BEGIN, @gl.Begin_);
  506. if ATextured then
  507. gluTessCallback(Tess, GLU_TESS_VERTEX, @TessIssueVertex)
  508. else
  509. gluTessCallback(Tess, GLU_TESS_VERTEX, @gl.Vertex3fv);
  510. gluTessCallback(Tess, GLU_TESS_END, @gl.End_);
  511. gluTessCallback(Tess, GLU_TESS_ERROR, @TessError);
  512. gluTessCallback(Tess, GLU_TESS_COMBINE, @TessCombine);
  513. NbExtraVertices := 0;
  514. // Issue normal
  515. if Assigned(ANormal) then
  516. begin
  517. gl.Normal3fv(PGLFloat(ANormal));
  518. gluTessNormal(Tess, ANormal^.X, ANormal^.Y, ANormal^.Z);
  519. end;
  520. // Issue polygon
  521. gluTessBeginPolygon(Tess, nil);
  522. gluTessBeginContour(Tess);
  523. if ASplineDivisions <= 1 then
  524. begin
  525. // no spline, use direct coordinates
  526. GetMem(NewVertices, Count * SizeOf(TAffineVector));
  527. if AInvertNormals then
  528. begin
  529. for I := Count - 1 downto 0 do
  530. begin
  531. SetVector(DblVector, PAffineVector(Items[I].AsAddress)^);
  532. gluTessVertex(Tess, DblVector, Items[I].AsAddress);
  533. end;
  534. end
  535. else
  536. begin
  537. for I := 0 to Count - 1 do
  538. begin
  539. SetVector(DblVector, PAffineVector(Items[I].AsAddress)^);
  540. gluTessVertex(Tess, DblVector, Items[I].AsAddress);
  541. end;
  542. end;
  543. end
  544. else
  545. begin
  546. // cubic spline
  547. GetMem(NewVertices, 2 * ASplineDivisions * Count * SizeOf(TAffineVector));
  548. Spline := CreateNewCubicSpline;
  549. F := 1.0 / ASplineDivisions;
  550. if AInvertNormals then
  551. begin
  552. for I := ASplineDivisions * (Count - 1) downto 0 do
  553. begin
  554. SplinePos := AllocNewVertex;
  555. Spline.SplineAffineVector(I * F, SplinePos^);
  556. SetVector(DblVector, SplinePos^);
  557. gluTessVertex(Tess, DblVector, SplinePos);
  558. end;
  559. end
  560. else
  561. begin
  562. for I := 0 to ASplineDivisions * (Count - 1) do
  563. begin
  564. SplinePos := AllocNewVertex;
  565. Spline.SplineAffineVector(I * F, SplinePos^);
  566. SetVector(DblVector, SplinePos^);
  567. gluTessVertex(Tess, DblVector, SplinePos);
  568. end;
  569. end;
  570. Spline.Free;
  571. end;
  572. gluTessEndContour(Tess);
  573. gluTessEndPolygon(Tess);
  574. // release stuff
  575. if Assigned(NewVertices) then
  576. FreeMem(NewVertices);
  577. gluDeleteTess(Tess);
  578. end;
  579. end;
  580. end.