GLMeshLines.pas 26 KB

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