GLS.MeshLines.pas 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.MeshLines;
  5. (* Line implementation by means of a Triangle strip. *)
  6. interface
  7. uses
  8. Winapi.OpenGL,
  9. System.Classes,
  10. System.SysUtils,
  11. Stage.OpenGLTokens,
  12. Stage.VectorTypes,
  13. Stage.VectorGeometry,
  14. Stage.Spline,
  15. GLS.VectorLists,
  16. GLS.Scene,
  17. GLS.Objects,
  18. GLS.Texture,
  19. GLS.Coordinates,
  20. GLS.Context,
  21. GLS.Material,
  22. GLS.Color,
  23. GLS.State,
  24. GLS.Nodes,
  25. GLS.VectorFileObjects,
  26. GLS.RenderContextInfo;
  27. type
  28. TGLLineOperation = (loSelectLine, loNewLine, loInsertNode, loMoveNode);
  29. type
  30. // Specialized Node for use in a TGLLines objects. Adds a Width property
  31. TGLLineNode = class(TGLNode)
  32. private
  33. FData: Pointer;
  34. protected
  35. public
  36. constructor Create(Collection: TCollection); override;
  37. destructor Destroy; override;
  38. procedure Assign(Source: TPersistent); override;
  39. property Data: Pointer read FData write FData;
  40. published
  41. end;
  42. // Specialized collection for Nodes in TGLMeshLines objects. Stores TGLLineNode items.
  43. TGLLineNodes = class(TGLNodes)
  44. public
  45. constructor Create(AOwner: TComponent); overload;
  46. destructor Destroy; override;
  47. procedure NotifyChange; override;
  48. function IndexOf(LineNode: TGLLineNode): Integer;
  49. end;
  50. TGLLineItem = class(TCollectionItem)
  51. private
  52. FName: String;
  53. FBreakAngle: Single;
  54. FDivision: Integer;
  55. FNodes: TGLLineNodes;
  56. FSplineMode: TGLLineSplineMode;
  57. FTextureLength: Single;
  58. FWidth: Single;
  59. FTextureCorrection: Boolean;
  60. FHide: Boolean;
  61. FData: Pointer;
  62. procedure SetHide(const Value: Boolean);
  63. procedure SetTextureCorrection(const Value: Boolean);
  64. procedure SetBreakAngle(const Value: Single);
  65. procedure SetDivision(const Value: Integer);
  66. procedure SetNodes(const Value: TGLLineNodes);
  67. procedure SetSplineMode(const Value: TGLLineSplineMode);
  68. procedure SetTextureLength(const Value: Single);
  69. procedure SetWidth(const Value: Single);
  70. protected
  71. procedure DoChanged; virtual;
  72. public
  73. property Data: Pointer read FData write FData;
  74. published
  75. constructor Create(Collection: TCollection); override;
  76. destructor Destroy; override;
  77. property Hide: Boolean read FHide write SetHide;
  78. property Name: String read FName write FName;
  79. property TextureCorrection: Boolean read FTextureCorrection
  80. write SetTextureCorrection;
  81. property BreakAngle: Single read FBreakAngle write SetBreakAngle;
  82. property Division: Integer read FDivision write SetDivision;
  83. property Nodes: TGLLineNodes read FNodes write SetNodes;
  84. property SplineMode: TGLLineSplineMode read FSplineMode write SetSplineMode;
  85. property TextureLength: Single read FTextureLength write SetTextureLength;
  86. property Width: Single read FWidth write SetWidth;
  87. end;
  88. TGLLineCollection = class(TOwnedCollection)
  89. private
  90. procedure SetItems(Index: Integer; const Val: TGLLineItem);
  91. function GetItems(Index: Integer): TGLLineItem;
  92. protected
  93. public
  94. function Add: TGLLineItem; overload;
  95. function Add(Name: String): TGLLineItem; overload;
  96. property Items[Index: Integer]: TGLLineItem read GetItems
  97. write SetItems; default;
  98. published
  99. end;
  100. TGLLightmapBounds = class(TGLCustomCoordinates)
  101. private
  102. function GetLeft: TGLFloat;
  103. function GetTop: TGLFloat;
  104. function GetRight: TGLFloat;
  105. function GetBottom: TGLFloat;
  106. function GetWidth: TGLFloat;
  107. function GetHeight: TGLFloat;
  108. procedure SetLeft(const Value: TGLFloat);
  109. procedure SetTop(const Value: TGLFloat);
  110. procedure SetRight(const Value: TGLFloat);
  111. procedure SetBottom(const Value: TGLFloat);
  112. published
  113. property Left: TGLFloat read GetLeft write SetLeft stored False;
  114. property Top: TGLFloat read GetTop write SetTop stored False;
  115. property Right: TGLFloat read GetRight write SetRight stored False;
  116. property Bottom: TGLFloat read GetBottom write SetBottom stored False;
  117. property Width: TGLFloat read GetWidth;
  118. property Height: TGLFloat read GetHeight;
  119. end;
  120. TGLMeshLines = class(TGLFreeForm)
  121. private
  122. FLines: TGLLineCollection;
  123. FMesh: TGLMeshObject;
  124. FLightmapBounds: TGLLightmapBounds;
  125. FLightmapIndex: Integer;
  126. FLightmapMaterialName: String;
  127. FFaceGroup: TFGVertexIndexList;
  128. FIndex: Integer;
  129. FNoZWrite: Boolean;
  130. FShowNodes: Boolean;
  131. FUpdating: Integer;
  132. FSelectedLineItem: TGLLineItem;
  133. FSelectedNode: TGLLineNode;
  134. FNode1, FNode2: TGLLineNode;
  135. function GetUpdating: Boolean;
  136. function PointNearLine(const LineItem: TGLLineItem; const X, Z: Single;
  137. Tolerance: Single = 1): Boolean;
  138. function PointNearSegment(const StartNode, EndNode: TGLLineNode;
  139. const X, Z: Single; LineWidth: Single; Tolerance: Single = 1): Boolean;
  140. procedure StitchStrips(idx: TGLIntegerList);
  141. procedure AddStitchMarker(idx: TGLIntegerList);
  142. procedure SetShowNodes(const Value: Boolean);
  143. procedure SetNoZWrite(const Value: Boolean);
  144. procedure SetLightmapIndex(const Value: Integer);
  145. procedure SetLightmapMaterialName(const Value: String);
  146. procedure SetLightmapBounds(const Value: TGLLightmapBounds);
  147. procedure DoChanged;
  148. procedure AddIndex;
  149. procedure AddVertices(const Up, Inner, Outer: TAffineVector; S: Single;
  150. Correction: Single; UseDegenerate: Boolean; LineItem: TGLLineItem);
  151. procedure BuildLineItem(LineItem: TGLLineItem);
  152. procedure BuildGeometry;
  153. procedure DrawNode(var rci: TGLRenderContextInfo; Node: TGLLineNode;
  154. LineWidth: Single);
  155. procedure DrawCircle(Radius: Single);
  156. function SelectNode(LineItem: TGLLineItem; X, Z: Single): TGLLineNode;
  157. protected
  158. procedure Loaded; override;
  159. public
  160. procedure BeginUpdate;
  161. procedure EndUpdate;
  162. procedure Clear;
  163. function SelectLineItem(const X, Z: Single; Tolerance: Single = 1)
  164. : TGLLineItem; overload;
  165. function SelectLineItem(LineItem: TGLLineItem): TGLLineItem; overload;
  166. function SelectLineItem(LineNode: TGLLineNode): TGLLineItem; overload;
  167. procedure DeselectLineItem;
  168. procedure DeselectLineNode;
  169. procedure BuildList(var rci: TGLRenderContextInfo); override;
  170. procedure DoRender(var rci: TGLRenderContextInfo;
  171. renderSelf, renderChildren: Boolean); override;
  172. procedure NotifyChange(Sender: TObject); override;
  173. property SelectedLineItem: TGLLineItem read FSelectedLineItem;
  174. property SelectedNode: TGLLineNode read FSelectedNode;
  175. property Node1: TGLLineNode read FNode1;
  176. property Node2: TGLLineNode read FNode2;
  177. published
  178. constructor Create(AOwner: TComponent); override;
  179. destructor Destroy; override;
  180. property Updating: Boolean Read GetUpdating;
  181. property Lines: TGLLineCollection read FLines;
  182. property Material;
  183. property LightmapBounds: TGLLightmapBounds read FLightmapBounds
  184. write SetLightmapBounds;
  185. property LightmapIndex: Integer read FLightmapIndex write SetLightmapIndex;
  186. property LightmapMaterialName: String read FLightmapMaterialName
  187. write SetLightmapMaterialName;
  188. property NoZWrite: Boolean read FNoZWrite write SetNoZWrite;
  189. property ShowNodes: Boolean read FShowNodes write SetShowNodes;
  190. end;
  191. // --------------------------------------------------------------------------
  192. implementation
  193. // --------------------------------------------------------------------------
  194. const
  195. CIRCLESEGMENTS = 32;
  196. constructor TGLLineNode.Create(Collection: TCollection);
  197. begin
  198. inherited Create(Collection);
  199. end;
  200. destructor TGLLineNode.Destroy;
  201. begin
  202. inherited Destroy;
  203. end;
  204. procedure TGLLineNode.Assign(Source: TPersistent);
  205. begin
  206. if Source is TGLLineNode then
  207. begin
  208. FData := TGLLineNode(Source).FData;
  209. end;
  210. inherited;
  211. end;
  212. constructor TGLLineNodes.Create(AOwner: TComponent);
  213. begin
  214. inherited Create(AOwner, TGLLineNode);
  215. end;
  216. destructor TGLLineNodes.Destroy;
  217. begin
  218. inherited;
  219. end;
  220. procedure TGLLineNodes.NotifyChange;
  221. begin
  222. if (GetOwner <> nil) then
  223. TGLMeshLines((GetOwner as TGLLineItem).Collection.Owner).StructureChanged;
  224. end;
  225. function TGLLineNodes.IndexOf(LineNode: TGLLineNode): Integer;
  226. var
  227. i: Integer;
  228. begin
  229. result := -1;
  230. if assigned(LineNode) then
  231. begin
  232. for i := 0 to Count - 1 do
  233. begin
  234. if LineNode = Items[i] then
  235. begin
  236. result := i;
  237. break;
  238. end;
  239. end;
  240. end;
  241. end;
  242. function TGLLineCollection.GetItems(Index: Integer): TGLLineItem;
  243. begin
  244. result := TGLLineItem(inherited Items[index]);
  245. end;
  246. procedure TGLLineCollection.SetItems(Index: Integer; const Val: TGLLineItem);
  247. begin
  248. inherited Items[index] := Val;
  249. end;
  250. function TGLLineCollection.Add: TGLLineItem;
  251. begin
  252. result := TGLLineItem.Create(self);
  253. end;
  254. function TGLLineCollection.Add(Name: String): TGLLineItem;
  255. begin
  256. result := Add;
  257. result.Name := Name;
  258. end;
  259. constructor TGLLineItem.Create(Collection: TCollection);
  260. begin
  261. inherited;
  262. FNodes := TGLLineNodes.Create(self, TGLLineNode);
  263. FBreakAngle := 30;
  264. FDivision := 10;
  265. FSplineMode := lsmLines;
  266. FTextureLength := 1;
  267. FWidth := 1;
  268. FTextureCorrection := False;
  269. end;
  270. destructor TGLLineItem.Destroy;
  271. begin
  272. if TGLMeshLines(Collection.Owner).SelectedLineItem = self then
  273. TGLMeshLines(Collection.Owner).DeselectLineItem;
  274. FNodes.Free;
  275. inherited;
  276. end;
  277. procedure TGLLineItem.SetHide(const Value: Boolean);
  278. begin
  279. FHide := Value;
  280. DoChanged;
  281. end;
  282. procedure TGLLineItem.SetTextureCorrection(const Value: Boolean);
  283. begin
  284. FTextureCorrection := Value;
  285. DoChanged;
  286. end;
  287. procedure TGLLineItem.SetBreakAngle(const Value: Single);
  288. begin
  289. FBreakAngle := Value;
  290. DoChanged;
  291. end;
  292. procedure TGLLineItem.SetDivision(const Value: Integer);
  293. begin
  294. FDivision := Value;
  295. DoChanged;
  296. end;
  297. procedure TGLLineItem.SetNodes(const Value: TGLLineNodes);
  298. begin
  299. FNodes.Assign(Value);
  300. DoChanged;
  301. end;
  302. procedure TGLLineItem.SetSplineMode(const Value: TGLLineSplineMode);
  303. begin
  304. FSplineMode := Value;
  305. DoChanged;
  306. end;
  307. procedure TGLLineItem.SetTextureLength(const Value: Single);
  308. begin
  309. FTextureLength := Value;
  310. DoChanged;
  311. end;
  312. procedure TGLLineItem.SetWidth(const Value: Single);
  313. begin
  314. FWidth := Value;
  315. DoChanged;
  316. end;
  317. procedure TGLLineItem.DoChanged;
  318. begin
  319. // Notify parent of change because the mesh needs to be regenerated
  320. if (GetOwner <> nil) then
  321. TGLMeshLines(Collection.Owner).NotifyChange(self);
  322. end;
  323. // ---------------------------------
  324. // TGLLightmapBounds
  325. // ---------------------------------
  326. function TGLLightmapBounds.GetLeft: TGLFloat;
  327. begin
  328. result := X;
  329. end;
  330. function TGLLightmapBounds.GetTop: TGLFloat;
  331. begin
  332. result := Y;
  333. end;
  334. function TGLLightmapBounds.GetRight: TGLFloat;
  335. begin
  336. result := Z;
  337. end;
  338. function TGLLightmapBounds.GetBottom: TGLFloat;
  339. begin
  340. result := W;
  341. end;
  342. function TGLLightmapBounds.GetWidth: TGLFloat;
  343. begin
  344. result := Z - X;
  345. end;
  346. function TGLLightmapBounds.GetHeight: TGLFloat;
  347. begin
  348. result := W - Y;
  349. end;
  350. procedure TGLLightmapBounds.SetLeft(const Value: TGLFloat);
  351. begin
  352. X := Value;
  353. end;
  354. procedure TGLLightmapBounds.SetTop(const Value: TGLFloat);
  355. begin
  356. Y := Value;
  357. end;
  358. procedure TGLLightmapBounds.SetRight(const Value: TGLFloat);
  359. begin
  360. Z := Value;
  361. end;
  362. procedure TGLLightmapBounds.SetBottom(const Value: TGLFloat);
  363. begin
  364. W := Value;
  365. end;
  366. // --------------------------------
  367. // TGLMeshLine
  368. // --------------------------------
  369. constructor TGLMeshLines.Create(AOwner: TComponent);
  370. begin
  371. inherited;
  372. FLines := TGLLineCollection.Create(self, TGLLineItem);
  373. FLightmapBounds := TGLLightmapBounds.Create(self);
  374. end;
  375. destructor TGLMeshLines.Destroy;
  376. begin
  377. FLines.Free;
  378. FLightmapBounds.Free;
  379. inherited;
  380. end;
  381. procedure TGLMeshLines.Loaded;
  382. begin
  383. DoChanged;
  384. end;
  385. procedure TGLMeshLines.BeginUpdate;
  386. begin
  387. inc(FUpdating);
  388. end;
  389. procedure TGLMeshLines.EndUpdate;
  390. begin
  391. Dec(FUpdating);
  392. if FUpdating < 1 then
  393. begin
  394. FUpdating := 0;
  395. DoChanged;
  396. end;
  397. end;
  398. procedure TGLMeshLines.Clear;
  399. begin
  400. FSelectedLineItem := nil;
  401. FSelectedNode := nil;
  402. FLines.Clear;
  403. MeshObjects.Clear;
  404. StructureChanged;
  405. end;
  406. procedure TGLMeshLines.BuildList(var rci: TGLRenderContextInfo);
  407. var
  408. i, j: Integer;
  409. begin
  410. inherited;
  411. if FShowNodes then
  412. begin
  413. for i := 0 to Lines.Count - 1 do
  414. begin
  415. if Lines[i] = FSelectedLineItem then
  416. begin
  417. for j := 0 to Lines[i].Nodes.Count - 1 do
  418. DrawNode(rci, TGLLineNode(Lines[i].Nodes[j]), Lines[i].Width);
  419. end;
  420. end;
  421. end;
  422. end;
  423. procedure TGLMeshLines.DoRender(var rci: TGLRenderContextInfo;
  424. renderSelf, renderChildren: Boolean);
  425. begin
  426. if FNoZWrite then
  427. begin
  428. gl.Disable(GL_Depth_Test);
  429. inherited;
  430. gl.Enable(GL_Depth_Test);
  431. end
  432. else
  433. inherited;
  434. end;
  435. procedure TGLMeshLines.SetShowNodes(const Value: Boolean);
  436. begin
  437. FShowNodes := Value;
  438. DoChanged;
  439. end;
  440. procedure TGLMeshLines.SetNoZWrite(const Value: Boolean);
  441. begin
  442. FNoZWrite := Value;
  443. DoChanged;
  444. end;
  445. procedure TGLMeshLines.SetLightmapIndex(const Value: Integer);
  446. begin
  447. FLightmapIndex := Value;
  448. DoChanged;
  449. end;
  450. procedure TGLMeshLines.SetLightmapMaterialName(const Value: String);
  451. var
  452. lLibMaterial: TGLLibMaterial;
  453. begin
  454. if Value <> '' then
  455. begin
  456. if assigned(LightmapLibrary) then
  457. begin
  458. lLibMaterial := LightmapLibrary.Materials.GetLibMaterialByName(Value);
  459. if assigned(lLibMaterial) then
  460. begin
  461. FLightmapIndex := lLibMaterial.ID;
  462. FLightmapMaterialName := Value;
  463. DoChanged;
  464. end;
  465. end;
  466. end;
  467. end;
  468. procedure TGLMeshLines.SetLightmapBounds(const Value: TGLLightmapBounds);
  469. begin
  470. FLightmapBounds.SetVector(Value.X, Value.Y, Value.Z, Value.W);
  471. DoChanged;
  472. end;
  473. procedure TGLMeshLines.DoChanged;
  474. begin
  475. if Updating then
  476. exit;
  477. BuildGeometry;
  478. StructureChanged;
  479. end;
  480. procedure TGLMeshLines.BuildGeometry;
  481. var
  482. i: Integer;
  483. lFirstLineDone: Boolean;
  484. lVertex: TAffineVector;
  485. lTextPoint: TTexPoint;
  486. begin
  487. if Updating then
  488. exit;
  489. // clear the mesh
  490. FMeshObjects.Clear;
  491. lFirstLineDone := False;
  492. FMesh := TGLMeshObject.CreateOwned(FMeshObjects);
  493. FMesh.Mode := momFaceGroups;
  494. FFaceGroup := TFGVertexIndexList.CreateOwned(FMesh.FaceGroups);
  495. FFaceGroup.Mode := fgmmTriangleStrip;
  496. FFaceGroup.LightmapIndex := FLightmapIndex;
  497. FIndex := 0;
  498. for i := 0 to Lines.Count - 1 do
  499. begin
  500. if not FLines.Items[i].Hide then
  501. begin
  502. if lFirstLineDone then
  503. AddStitchMarker(FFaceGroup.VertexIndices);
  504. if TGLLineItem(FLines.Items[i]).Nodes.Count > 0 then
  505. begin
  506. BuildLineItem(TGLLineItem(FLines.Items[i]));
  507. lFirstLineDone := True;
  508. end;
  509. end;
  510. end;
  511. StitchStrips(FFaceGroup.VertexIndices);
  512. // Calculate lightmapping
  513. if assigned(LightmapLibrary) and (LightmapIndex <> -1) then
  514. for i := 0 to FMesh.Vertices.Count - 1 do
  515. begin
  516. lVertex := FMesh.Vertices.Items[i];
  517. lTextPoint.S := (lVertex.X - FLightmapBounds.Left) /
  518. FLightmapBounds.Width;
  519. lTextPoint.t := (lVertex.Z - FLightmapBounds.Top) /
  520. FLightmapBounds.Height;
  521. FMesh.LightMapTexCoords.Add(lTextPoint);
  522. end;
  523. end;
  524. procedure TGLMeshLines.DrawNode(var rci: TGLRenderContextInfo;
  525. Node: TGLLineNode; LineWidth: Single);
  526. var
  527. lNodeSize: Single;
  528. begin
  529. lNodeSize := LineWidth * 0.7;
  530. gl.PushMatrix;
  531. gl.Translatef(Node.X, Node.Y, Node.Z);
  532. if lNodeSize <> 1 then
  533. begin
  534. gl.PushMatrix;
  535. gl.Scalef(lNodeSize, lNodeSize, lNodeSize);
  536. /// rci.GLStates.UnSetGLState(stTexture2D);
  537. rci.GLStates.Disable(stColorMaterial);
  538. rci.GLStates.Disable(stBlend);
  539. if Node = FSelectedNode then
  540. rci.GLStates.SetGLMaterialColors(cmFRONT, clrBlack, clrGray20, clrYellow,
  541. clrBlack, 0)
  542. else
  543. rci.GLStates.SetGLMaterialColors(cmFRONT, clrBlack, clrGray20, clrGreen,
  544. clrBlack, 0);
  545. DrawCircle(lNodeSize);
  546. gl.PopMatrix;
  547. end
  548. else
  549. begin
  550. if Node = FSelectedNode then
  551. rci.GLStates.SetGLMaterialColors(cmFRONT, clrBlack, clrGray20, clrYellow,
  552. clrBlack, 0)
  553. else
  554. rci.GLStates.SetGLMaterialColors(cmFRONT, clrBlack, clrGray20, clrGreen,
  555. clrBlack, 0);
  556. DrawCircle(lNodeSize);
  557. end;
  558. gl.PopMatrix;
  559. end;
  560. procedure TGLMeshLines.DrawCircle(Radius: Single);
  561. var
  562. Inner, Outer, p1, p2: TGLVector;
  563. i: Integer;
  564. a: Single;
  565. lUp: TAffineVector;
  566. begin
  567. Inner := VectorMake(1, 0, 0);
  568. Outer := VectorMake(1.3, 0, 0);
  569. gl.Begin_(GL_TRIANGLE_STRIP);
  570. for i := 0 to CIRCLESEGMENTS do
  571. begin
  572. a := i * 2 * pi / CIRCLESEGMENTS;
  573. p1 := Outer;
  574. p2 := Inner;
  575. lUp := Up.AsAffineVector;
  576. RotateVector(p1, lUp, a);
  577. RotateVector(p2, lUp, a);
  578. gl.Vertex3fv(@p1.X);
  579. gl.Vertex3fv(@p2.X);
  580. end;
  581. gl.End_();
  582. end;
  583. function TGLMeshLines.SelectNode(LineItem: TGLLineItem; X, Z: Single)
  584. : TGLLineNode;
  585. var
  586. i: Integer;
  587. lRange: Single;
  588. length: Single;
  589. begin
  590. result := nil;
  591. lRange := LineItem.Width * 0.88;
  592. for i := 0 to LineItem.Nodes.Count - 1 do
  593. begin
  594. length := 1 / RLength((X - LineItem.Nodes[i].X), (Z - LineItem.Nodes[i].Z));
  595. if length < lRange then
  596. begin
  597. result := TGLLineNode(LineItem.Nodes[i]);
  598. break;
  599. end;
  600. end;
  601. end;
  602. function TGLMeshLines.SelectLineItem(LineItem: TGLLineItem): TGLLineItem;
  603. begin
  604. result := nil;
  605. FSelectedLineItem := LineItem;
  606. FSelectedNode := nil;
  607. DoChanged;
  608. end;
  609. function TGLMeshLines.SelectLineItem(LineNode: TGLLineNode): TGLLineItem;
  610. begin
  611. FSelectedLineItem := TGLLineItem(LineNode.Collection.Owner);
  612. FSelectedNode := LineNode;
  613. result := FSelectedLineItem;
  614. DoChanged;
  615. end;
  616. procedure TGLMeshLines.DeselectLineItem;
  617. begin
  618. FSelectedLineItem := nil;
  619. FSelectedNode := nil;
  620. DoChanged;
  621. end;
  622. procedure TGLMeshLines.DeselectLineNode;
  623. begin
  624. FSelectedNode := nil;
  625. DoChanged;
  626. end;
  627. function TGLMeshLines.SelectLineItem(const X, Z: Single; Tolerance: Single = 1)
  628. : TGLLineItem;
  629. var
  630. i: Integer;
  631. lStartPoint: Integer;
  632. lNode: TGLLineNode;
  633. lNodeWasSelected: Boolean;
  634. begin
  635. result := nil;
  636. lNodeWasSelected := False;
  637. if assigned(FSelectedLineItem) and not lNodeWasSelected then
  638. lStartPoint := FSelectedLineItem.ID + 1
  639. else
  640. lStartPoint := 0;
  641. for i := lStartPoint to FLines.Count - 1 do
  642. begin
  643. if (FLines[i] <> FSelectedLineItem) or lNodeWasSelected then
  644. begin
  645. if PointNearLine(FLines[i], X, Z, Tolerance) then
  646. begin
  647. result := FLines[i];
  648. lNode := SelectNode(FLines[i], X, Z);
  649. if lNode <> FSelectedNode then
  650. begin
  651. FSelectedNode := lNode;
  652. end;
  653. break;
  654. end;
  655. end;
  656. end;
  657. if not assigned(result) then
  658. begin
  659. for i := 0 to lStartPoint - 1 do
  660. begin
  661. if FLines[i] <> FSelectedLineItem then
  662. begin
  663. if PointNearLine(FLines[i], X, Z, Tolerance) then
  664. begin
  665. result := FLines[i];
  666. break;
  667. end;
  668. end;
  669. end;
  670. end;
  671. FSelectedLineItem := result;
  672. if not assigned(FSelectedLineItem) then
  673. begin
  674. FSelectedNode := nil;
  675. FNode1 := nil;
  676. FNode2 := nil;
  677. end;
  678. DoChanged;
  679. end;
  680. function TGLMeshLines.GetUpdating: Boolean;
  681. begin
  682. result := FUpdating > 0;
  683. end;
  684. function TGLMeshLines.PointNearLine(const LineItem: TGLLineItem;
  685. const X, Z: Single; Tolerance: Single = 1): Boolean;
  686. var
  687. i: Integer;
  688. lStartNode, lEndNode: TGLLineNode;
  689. begin
  690. result := False;
  691. for i := 0 to LineItem.Nodes.Count - 2 do
  692. begin
  693. lStartNode := TGLLineNode(LineItem.Nodes[i]);
  694. lEndNode := TGLLineNode(LineItem.Nodes[i + 1]);
  695. if PointNearSegment(lStartNode, lEndNode, X, Z, LineItem.Width, Tolerance)
  696. then
  697. begin
  698. result := True;
  699. FNode1 := lStartNode;
  700. FNode2 := lEndNode;
  701. break;
  702. end;
  703. end;
  704. end;
  705. function TGLMeshLines.PointNearSegment(const StartNode, EndNode: TGLLineNode;
  706. const X, Z: Single; LineWidth: Single; Tolerance: Single = 1): Boolean;
  707. var
  708. xt, yt, u, len: Single;
  709. xp, yp: Single;
  710. lDist: Single;
  711. begin
  712. result := False;
  713. lDist := (LineWidth / 2) * Tolerance;
  714. xt := EndNode.X - StartNode.X;
  715. yt := EndNode.Z - StartNode.Z;
  716. len := sqrt(xt * xt + yt * yt);
  717. xp := (X - StartNode.X);
  718. yp := (Z - StartNode.Z);
  719. u := (xp * xt + yp * yt) / len;
  720. // point beyond line
  721. if (u < -lDist) or (u > len + lDist) then
  722. exit;
  723. u := u / len;
  724. // get the point on the line that's pependicular to the point in question
  725. xt := StartNode.X + xt * u;
  726. yt := StartNode.Z + yt * u;
  727. // find the distance to the line, and see if it's closer than the specified distance
  728. result := sqrt(sqr(xt - X) + sqr(yt - Z)) <= lDist;
  729. end;
  730. procedure TGLMeshLines.StitchStrips(idx: TGLIntegerList);
  731. var
  732. i: Integer;
  733. i0, i1, i2: Integer;
  734. begin
  735. for i := idx.Count - 1 downto 0 do
  736. begin
  737. if idx[i] = -1 then
  738. begin
  739. i0 := idx[i - 1];
  740. i1 := idx[i + 4];
  741. i2 := idx[i + 5];
  742. idx[i] := i0;
  743. idx[i + 1] := i1;
  744. idx[i + 2] := i1;
  745. idx[i + 3] := i2;
  746. end;
  747. end;
  748. end;
  749. procedure TGLMeshLines.AddStitchMarker(idx: TGLIntegerList);
  750. begin
  751. idx.Add(-1);
  752. idx.Add(-2);
  753. idx.Add(-2);
  754. idx.Add(-2);
  755. end;
  756. procedure TGLMeshLines.NotifyChange(Sender: TObject);
  757. begin
  758. inherited;
  759. DoChanged;
  760. end;
  761. procedure TGLMeshLines.AddIndex;
  762. begin
  763. FFaceGroup.Add(FIndex);
  764. inc(FIndex);
  765. end;
  766. procedure TGLMeshLines.AddVertices(const Up, Inner, Outer: TAffineVector;
  767. S: Single; Correction: Single; UseDegenerate: Boolean; LineItem: TGLLineItem);
  768. begin
  769. if not LineItem.TextureCorrection then
  770. Correction := 0
  771. else
  772. Correction := Correction / (LineItem.TextureLength / LineItem.Width);
  773. FMesh.Normals.Add(Up);
  774. FMesh.Vertices.Add(Outer);
  775. FMesh.TexCoords.Add(S - Correction, 1);
  776. AddIndex;
  777. FMesh.Normals.Add(Up);
  778. FMesh.TexCoords.Add(S + Correction, 0);
  779. FMesh.Vertices.Add(Inner);
  780. AddIndex;
  781. if LineItem.TextureCorrection then
  782. begin
  783. FMesh.Normals.Add(Up);
  784. FMesh.Vertices.Add(Outer);
  785. FMesh.TexCoords.Add(S + Correction, 1);
  786. AddIndex;
  787. FMesh.Normals.Add(Up);
  788. FMesh.TexCoords.Add(S - Correction, 0);
  789. FMesh.Vertices.Add(Inner);
  790. AddIndex;
  791. end;
  792. end;
  793. procedure TGLMeshLines.BuildLineItem(LineItem: TGLLineItem);
  794. var
  795. Seg1: TAffineVector;
  796. Seg2: TAffineVector;
  797. NSeg1: TAffineVector;
  798. NSeg2: TAffineVector;
  799. N1, N2, N3: TAffineVector;
  800. Inner: TAffineVector;
  801. Outer: TAffineVector;
  802. lUp: TAffineVector;
  803. lAngle: Single;
  804. lAngleOffset: Single;
  805. lTotalAngleChange: Single;
  806. lBreakAngle: Single;
  807. i: Integer;
  808. Flip: Boolean;
  809. S: Single;
  810. lSpline: TCubicSpline;
  811. lCount: Integer;
  812. f: Single;
  813. a, b, c: Single;
  814. lHalfLineWidth: Single;
  815. begin
  816. inherited;
  817. lTotalAngleChange := 0;
  818. lHalfLineWidth := LineItem.Width / 2;
  819. lBreakAngle := DegToRadian(LineItem.BreakAngle);
  820. try
  821. N1 := AffineVectorMake(0, 0, 0);
  822. N2 := AffineVectorMake(0, 0, 0);
  823. N3 := AffineVectorMake(0, 0, 0);
  824. S := 0;
  825. f := 0;
  826. lSpline := nil;
  827. lUp := Up.AsAffineVector;
  828. lCount := 0;
  829. if LineItem.SplineMode = lsmLines then
  830. lCount := LineItem.Nodes.Count - 1
  831. else if LineItem.Nodes.Count > 1 then
  832. begin
  833. lCount := (LineItem.Nodes.Count - 1) * LineItem.Division;
  834. lSpline := LineItem.Nodes.CreateNewCubicSpline;
  835. f := 1 / LineItem.Division;
  836. end;
  837. for i := 0 to lCount do
  838. begin
  839. if LineItem.SplineMode = lsmLines then
  840. begin
  841. N3 := LineItem.Nodes.Items[i].AsAffineVector
  842. end
  843. else
  844. begin
  845. if lCount > 1 then
  846. begin
  847. lSpline.SplineXYZ(i * f, a, b, c);
  848. N3 := AffineVectorMake(a, b, c);
  849. end;
  850. end;
  851. if i > 0 then
  852. begin
  853. Seg1 := Seg2;
  854. Seg2 := VectorSubtract(N3, N2);
  855. end;
  856. if (i = 1) and not VectorEQuals(Seg2, NullVector) then
  857. begin
  858. // Create start vertices
  859. // this makes the assumption that these vectors are different which not always true
  860. Inner := VectorCrossProduct(Seg2, lUp);
  861. NormalizeVector(Inner);
  862. ScaleVector(Inner, lHalfLineWidth);
  863. Outer := VectorNegate(Inner);
  864. AddVector(Inner, N2);
  865. AddVector(Outer, N2);
  866. AddVertices(lUp, Inner, Outer, S, 0, False, LineItem);
  867. S := S + VectorLength(Seg2) / LineItem.TextureLength;
  868. end;
  869. if i > 1 then
  870. begin
  871. lUp := VectorCrossProduct(Seg2, Seg1);
  872. if VectorEQuals(lUp, NullVector) then
  873. lUp := Up.AsAffineVector;
  874. Flip := VectorAngleCosine(lUp, self.Up.AsAffineVector) < 0;
  875. if Flip then
  876. NegateVector(lUp);
  877. NSeg1 := VectorNormalize(Seg1);
  878. NSeg2 := VectorNormalize(Seg2);
  879. if VectorEQuals(NSeg1, NSeg2) then
  880. begin
  881. Inner := VectorCrossProduct(Seg2, lUp);
  882. lAngle := 0;
  883. end
  884. else
  885. begin
  886. Inner := VectorSubtract(NSeg2, NSeg1);
  887. lAngle := (1.5707963 - ArcCosine(VectorLength(Inner) / 2));
  888. end;
  889. lTotalAngleChange := lTotalAngleChange + (lAngle * 2);
  890. // Create intermediate vertices
  891. if (lTotalAngleChange > lBreakAngle) or (LineItem.BreakAngle = -1) then
  892. begin
  893. lTotalAngleChange := 0;
  894. // Correct width for angles less than 170
  895. if lAngle < 1.52 then
  896. lAngleOffset := lHalfLineWidth * sqrt(sqr(Tangent(lAngle)) + 1)
  897. else
  898. lAngleOffset := lHalfLineWidth;
  899. NormalizeVector(Inner);
  900. ScaleVector(Inner, lAngleOffset);
  901. Outer := VectorNegate(Inner);
  902. AddVector(Inner, N2);
  903. AddVector(Outer, N2);
  904. if not Flip then
  905. AddVertices(lUp, Inner, Outer, S, -Tangent(lAngle) / 2,
  906. True, LineItem)
  907. else
  908. AddVertices(lUp, Outer, Inner, S, Tangent(lAngle) / 2, True,
  909. LineItem);
  910. end;
  911. S := S + VectorLength(Seg2) / LineItem.TextureLength;
  912. end;
  913. // Create last vertices
  914. if (lCount > 0) and (i = lCount) and not VectorEQuals(Seg2, NullVector)
  915. then
  916. begin
  917. lUp := Up.AsAffineVector;
  918. Inner := VectorCrossProduct(Seg2, lUp);
  919. NormalizeVector(Inner);
  920. ScaleVector(Inner, lHalfLineWidth);
  921. Outer := VectorNegate(Inner);
  922. AddVector(Inner, N3);
  923. AddVector(Outer, N3);
  924. AddVertices(lUp, Inner, Outer, S, 0, False, LineItem);
  925. end;
  926. N1 := N2;
  927. N2 := N3;
  928. end;
  929. except
  930. on e: Exception do
  931. raise Exception.Create(e.Message);
  932. end;
  933. if assigned(lSpline) then
  934. lSpline.Free;
  935. end;
  936. // ---------------------------------
  937. initialization
  938. // ---------------------------------
  939. RegisterClasses([TGLMeshLines]);
  940. end.