GLS.Extrusion.pas 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Extrusion;
  5. (*
  6. Extrusion objects are solids defined by the surface described by a moving curve.
  7. Suggestion:
  8. All extrusion objects use actually the same kind of "parts",
  9. one common type should do.
  10. *)
  11. interface
  12. {$I GLScene.inc}
  13. uses
  14. Winapi.OpenGL,
  15. System.Classes,
  16. System.SysUtils,
  17. System.Math,
  18. GLS.OpenGLTokens,
  19. GLS.Context,
  20. GLS.Objects,
  21. GLS.Scene,
  22. GLS.MultiPolygon,
  23. GLS.Color,
  24. GLS.VectorGeometry,
  25. GLS.RenderContextInfo,
  26. GLS.Nodes,
  27. GLS.State,
  28. GLS.VectorTypes;
  29. type
  30. TGLExtrusionSolidPart = (espOutside, espInside, espStartPolygon, espStopPolygon);
  31. TGLExtrusionSolidParts = set of TGLExtrusionSolidPart;
  32. TGLRevolutionSolidPart = (rspOutside, rspInside, rspStartPolygon, rspStopPolygon);
  33. TGLRevolutionSolidParts = set of TGLRevolutionSolidPart;
  34. (* A solid object generated by rotating a curve along the Y axis.
  35. The curve is described by the Nodes and SplineMode properties, and it is
  36. rotated in the trigonometrical direction (CCW when seen from Y->INF).
  37. The TGLRevolutionSolid can also be used to render regular helicoidions, by
  38. setting a non-null YOffsetPerTurn, and adjusting start/finish angles to
  39. make more than one revolution.
  40. If you want top/bottom caps, just add a first/last node that will make
  41. the curve start/finish on the Y axis. *)
  42. TGLRevolutionSolid = class(TGLPolygonBase)
  43. private
  44. FSlices: Integer;
  45. FStartAngle, FStopAngle: Single;
  46. FNormals: TGLNormalSmoothing;
  47. FYOffsetPerTurn: Single;
  48. FTriangleCount: Integer;
  49. FNormalDirection: TGLNormalDirection;
  50. FParts: TGLRevolutionSolidParts;
  51. FAxisAlignedDimensionsCache: TGLVector;
  52. protected
  53. procedure SetStartAngle(const val: Single);
  54. procedure SetStopAngle(const val: Single);
  55. function StoreStopAngle: Boolean;
  56. procedure SetSlices(const val: Integer);
  57. procedure SetNormals(const val: TGLNormalSmoothing);
  58. procedure SetYOffsetPerTurn(const val: Single);
  59. procedure SetNormalDirection(const val: TGLNormalDirection);
  60. procedure SetParts(const val: TGLRevolutionSolidParts);
  61. public
  62. constructor Create(AOwner: TComponent); override;
  63. destructor Destroy; override;
  64. procedure Assign(Source: TPersistent); override;
  65. procedure BuildList(var rci: TGLRenderContextInfo); override;
  66. {Number of triangles used for rendering. }
  67. property TriangleCount: Integer read FTriangleCount;
  68. function AxisAlignedDimensionsUnscaled: TGLVector; override;
  69. procedure StructureChanged; override;
  70. published
  71. (* Parts of the rotation solid to be generated for rendering.
  72. rspInside and rspOutside are generated from the curve and make the
  73. inside/outside as long as NormalDirection=ndOutside and the solid
  74. is described by the curve that goes from top to bottom.
  75. Start/StopPolygon are tesselated from the curve (considered as closed). *)
  76. property Parts: TGLRevolutionSolidParts read FParts write SetParts default [rspOutside];
  77. property StartAngle: Single read FStartAngle write SetStartAngle;
  78. property StopAngle: Single read FStopAngle write SetStopAngle stored StoreStopAngle;
  79. (* Y offset applied to the curve position for each turn.
  80. This amount is applied proportionnally, for instance if your curve
  81. is a small circle, off from the Y axis, with a YOffset set to 0 (zero),
  82. you will get a torus, but with a non null value, you will get a
  83. small helicoidal spring.
  84. This can be useful for rendering, lots of helicoidal objects from
  85. screws, to nails to stairs etc. *)
  86. property YOffsetPerTurn: Single read FYOffsetPerTurn write
  87. SetYOffsetPerTurn;
  88. // Number of slices per turn (360deg).
  89. property Slices: Integer read FSlices write SetSlices default 16;
  90. property Normals: TGLNormalSmoothing read FNormals write SetNormals default
  91. nsFlat;
  92. property NormalDirection: TGLNormalDirection read FNormalDirection write
  93. SetNormalDirection default ndOutside;
  94. end;
  95. (* Extrudes a complex Polygon into Z direction.
  96. For contour description see TGLMultiPolygonBase.
  97. properties Parts, Height (or should we better cal it Depth, because its in Z?),
  98. Stacks, Normals and NormalDirection are equivalent to TGLRevolutionSolid.
  99. If Normals=nsSmooth and the angle between two consecutive normals along the
  100. contour is less than MinSmoothAngle, smoothing is done, otherweise flat normals
  101. are used. This makes it possible to have smooth normals on sharp edged contours. *)
  102. TGLExtrusionSolid = class(TGLMultiPolygonBase)
  103. private
  104. FStacks: Integer;
  105. FNormals: TGLNormalSmoothing;
  106. FTriangleCount: Integer;
  107. FNormalDirection: TGLNormalDirection;
  108. FParts: TGLExtrusionSolidParts;
  109. FHeight: TGLFloat;
  110. FMinSmoothAngle: Single;
  111. FMinSmoothAngleCos: Single;
  112. FAxisAlignedDimensionsCache: TGLVector;
  113. procedure SetHeight(const Value: TGLFloat);
  114. procedure SetMinSmoothAngle(const Value: Single);
  115. protected
  116. procedure SetStacks(const val: Integer);
  117. procedure SetNormals(const val: TGLNormalSmoothing);
  118. procedure SetNormalDirection(const val: TGLNormalDirection);
  119. procedure SetParts(const val: TGLExtrusionSolidParts);
  120. public
  121. constructor Create(AOwner: TComponent); override;
  122. destructor Destroy; override;
  123. procedure Assign(Source: TPersistent); override;
  124. procedure BuildList(var rci: TGLRenderContextInfo); override;
  125. // Number of triangles used for rendering.
  126. property TriangleCount: Integer read FTriangleCount;
  127. function AxisAlignedDimensionsUnscaled: TGLVector; override;
  128. procedure StructureChanged; override;
  129. published
  130. property Parts: TGLExtrusionSolidParts read FParts write SetParts default [espOutside];
  131. property Height: TGLFloat read FHeight write SetHeight;
  132. property Stacks: Integer read FStacks write SetStacks default 1;
  133. property Normals: TGLNormalSmoothing read FNormals write SetNormals default nsFlat;
  134. property NormalDirection: TGLNormalDirection read FNormalDirection write SetNormalDirection default ndOutside;
  135. property MinSmoothAngle: Single read FMinSmoothAngle write SetMinSmoothAngle;
  136. end;
  137. TGLPipeNode = class(TGLNode)
  138. private
  139. FRadiusFactor: Single;
  140. FColor: TGLColor;
  141. FTexCoordT: Single;
  142. protected
  143. function GetDisplayName: string; override;
  144. procedure SetRadiusFactor(const val: Single);
  145. function StoreRadiusFactor: Boolean;
  146. procedure SetColor(const val: TGLColor);
  147. procedure ColorChanged(sender: TObject);
  148. function StoreTexCoordT: Boolean;
  149. public
  150. constructor Create(Collection: TCollection); override;
  151. destructor Destroy; override;
  152. procedure Assign(Source: TPersistent); override;
  153. published
  154. property RadiusFactor: Single read FRadiusFactor write SetRadiusFactor stored
  155. StoreRadiusFactor;
  156. property Color: TGLColor read FColor write SetColor;
  157. property TexCoordT: Single read FTexCoordT write FTexCoordT stored
  158. StoreTexCoordT;
  159. end;
  160. TGLPipeNodes = class(TGLLinesNodes)
  161. protected
  162. procedure SetItems(index: Integer; const val: TGLPipeNode);
  163. function GetItems(index: Integer): TGLPipeNode;
  164. public
  165. constructor Create(AOwner: TComponent);
  166. function Add: TGLPipeNode;
  167. function FindItemID(ID: Integer): TGLPipeNode;
  168. property Items[index: Integer]: TGLPipeNode read GetItems write SetItems; default;
  169. end;
  170. TPipePart = (ppOutside, ppInside, ppStartDisk, ppStopDisk);
  171. TPipeParts = set of TPipePart;
  172. TPipeNodesColorMode = (pncmNone, pncmEmission, pncmAmbient, pncmDiffuse,
  173. pncmAmbientAndDiffuse);
  174. TPipeTexCoordMode = (ptcmDefault, ptcmManual);
  175. TPipeNormalMode = (pnmDefault, pnmAdvanced);
  176. (* A solid object generated by extruding a circle along a trajectory.
  177. Texture coordinates NOT supported yet. *)
  178. TGLPipe = class(TGLPolygonBase)
  179. private
  180. FSlices: Integer;
  181. FParts: TPipeParts;
  182. FTriangleCount: Integer;
  183. FRadius: Single;
  184. FNodesColorMode: TPipeNodesColorMode;
  185. FTextCoordMode: TPipeTexCoordMode;
  186. FTextCoordTileS: Single;
  187. FTextCoordTileT: Single;
  188. FNormalMode: TPipeNormalMode;
  189. FNormalSmoothAngle: Single;
  190. protected
  191. procedure CreateNodes; override;
  192. procedure SetSlices(const val: Integer);
  193. procedure SetParts(const val: TPipeParts);
  194. procedure SetRadius(const val: Single);
  195. function StoreRadius: Boolean;
  196. procedure SetNodesColorMode(const val: TPipeNodesColorMode);
  197. procedure SetTextCoordMode(const val: TPipeTexCoordMode);
  198. procedure SetTextCoordTileS(const val: Single);
  199. procedure SetTextCoordTileT(const val: Single);
  200. function StoreTextCoordTileS: Boolean;
  201. function StoreTextCoordTileT: Boolean;
  202. procedure SetNormalMode(const val: TPipeNormalMode);
  203. procedure SetNormalSmoothAngle(const val: Single);
  204. public
  205. constructor Create(AOwner: TComponent); override;
  206. destructor Destroy; override;
  207. procedure Assign(Source: TPersistent); override;
  208. procedure BuildList(var rci: TGLRenderContextInfo); override;
  209. // Number of triangles used for rendering.
  210. property TriangleCount: Integer read FTriangleCount;
  211. published
  212. property Parts: TPipeParts read FParts write SetParts default [ppOutside];
  213. property Slices: Integer read FSlices write SetSlices default 16;
  214. property Radius: Single read FRadius write SetRadius;
  215. property NodesColorMode: TPipeNodesColorMode read FNodesColorMode write
  216. SetNodesColorMode default pncmNone;
  217. property TexCoordMode: TPipeTexCoordMode read FTextCoordMode
  218. write SetTextCoordMode default ptcmDefault;
  219. property TexCoordTileS: Single read FTextCoordTileS write SetTextCoordTileS
  220. stored StoreTextCoordTileS;
  221. property TexCoordTileT: Single read FTextCoordTileT write SetTextCoordTileT
  222. stored StoreTextCoordTileT;
  223. property NormalMode: TPipeNormalMode read FNormalMode write SetNormalMode
  224. default pnmDefault;
  225. property NormalSmoothAngle: Single read FNormalSmoothAngle write
  226. SetNormalSmoothAngle;
  227. end;
  228. // ------------------------------------------------------------------
  229. implementation
  230. // ------------------------------------------------------------------
  231. uses
  232. GLS.Spline,
  233. GLS.VectorLists,
  234. GLS.XOpenGL;
  235. // ------------------
  236. // ------------------ TGLRevolutionSolid ------------------
  237. // ------------------
  238. constructor TGLRevolutionSolid.Create(AOwner: TComponent);
  239. begin
  240. inherited Create(AOwner);
  241. FStartAngle := 0;
  242. FStopAngle := 360;
  243. FSlices := 16;
  244. FNormals := nsFlat;
  245. FNormalDirection := ndOutside;
  246. FParts := [rspOutside];
  247. end;
  248. destructor TGLRevolutionSolid.Destroy;
  249. begin
  250. inherited Destroy;
  251. end;
  252. procedure TGLRevolutionSolid.SetStartAngle(const val: Single);
  253. begin
  254. if FStartAngle <> val then
  255. begin
  256. FStartAngle := val;
  257. if FStartAngle > FStopAngle then
  258. FStopAngle := FStartAngle;
  259. StructureChanged;
  260. end;
  261. end;
  262. procedure TGLRevolutionSolid.SetStopAngle(const val: Single);
  263. begin
  264. if FStopAngle <> val then
  265. begin
  266. FStopAngle := val;
  267. if FStopAngle < FStartAngle then
  268. FStartAngle := FStopAngle;
  269. StructureChanged;
  270. end;
  271. end;
  272. function TGLRevolutionSolid.StoreStopAngle: Boolean;
  273. begin
  274. Result := (FStopAngle <> 360);
  275. end;
  276. procedure TGLRevolutionSolid.SetSlices(const val: Integer);
  277. begin
  278. if (val <> FSlices) and (val > 0) then
  279. begin
  280. FSlices := val;
  281. StructureChanged;
  282. end;
  283. end;
  284. procedure TGLRevolutionSolid.SetNormals(const val: TGLNormalSmoothing);
  285. begin
  286. if FNormals <> val then
  287. begin
  288. FNormals := val;
  289. StructureChanged;
  290. end;
  291. end;
  292. procedure TGLRevolutionSolid.SetYOffsetPerTurn(const val: Single);
  293. begin
  294. if FYOffsetPerTurn <> val then
  295. begin
  296. FYOffsetPerTurn := val;
  297. StructureChanged;
  298. end;
  299. end;
  300. procedure TGLRevolutionSolid.SetNormalDirection(const val: TGLNormalDirection);
  301. begin
  302. if FNormalDirection <> val then
  303. begin
  304. FNormalDirection := val;
  305. StructureChanged;
  306. end;
  307. end;
  308. procedure TGLRevolutionSolid.SetParts(const val: TGLRevolutionSolidParts);
  309. begin
  310. if FParts <> val then
  311. begin
  312. FParts := val;
  313. StructureChanged;
  314. end;
  315. end;
  316. procedure TGLRevolutionSolid.Assign(Source: TPersistent);
  317. begin
  318. if Source is TGLRevolutionSolid then
  319. begin
  320. FStartAngle := TGLRevolutionSolid(Source).FStartAngle;
  321. FStopAngle := TGLRevolutionSolid(Source).FStopAngle;
  322. FSlices := TGLRevolutionSolid(Source).FSlices;
  323. FNormals := TGLRevolutionSolid(Source).FNormals;
  324. FYOffsetPerTurn := TGLRevolutionSolid(Source).FYOffsetPerTurn;
  325. FNormalDirection := TGLRevolutionSolid(Source).FNormalDirection;
  326. FParts := TGLRevolutionSolid(Source).FParts;
  327. end;
  328. inherited Assign(Source);
  329. end;
  330. procedure TGLRevolutionSolid.BuildList(var rci: TGLRenderContextInfo);
  331. var
  332. deltaAlpha, startAlpha, stopAlpha, alpha: Single;
  333. deltaS: Single;
  334. deltaYOffset, yOffset, startYOffset: Single;
  335. lastNormals: PAffineVectorArray;
  336. firstStep, gotYDeltaOffset: Boolean;
  337. procedure CalcNormal(const ptTop, ptBottom: PAffineVector; var normal:
  338. TAffineVector);
  339. var
  340. tb: TAffineVector;
  341. mx, mz: Single;
  342. begin
  343. mx := ptBottom^.X + ptTop^.X;
  344. mz := ptBottom^.Z + ptTop^.Z;
  345. VectorSubtract(ptBottom^, ptTop^, tb);
  346. normal.X := -tb.Y * mx;
  347. normal.Y := mx * tb.X + mz * tb.Z;
  348. normal.Z := -mz * tb.Y;
  349. NormalizeVector(normal);
  350. end;
  351. procedure BuildStep(ptTop, ptBottom: PAffineVector; invertNormals: Boolean;
  352. topT, bottomT: Single);
  353. var
  354. i: Integer;
  355. topBase, topNext, bottomBase, bottomNext, normal, topNormal, bottomNormal:
  356. TAffineVector;
  357. topTPBase, topTPNext, bottomTPBase, bottomTPNext: TTexPoint;
  358. nextAlpha: Single;
  359. ptBuffer: PAffineVector;
  360. procedure SetLocalNormals;
  361. begin
  362. if (FNormals = nsFlat) or FirstStep then
  363. begin
  364. topNormal := normal;
  365. bottomNormal := normal;
  366. if (FNormals = nsSmooth) then
  367. lastNormals^[i] := normal;
  368. end
  369. else if (FNormals = nsSmooth) then
  370. begin
  371. if invertNormals then
  372. begin
  373. topNormal := normal;
  374. bottomNormal := lastNormals^[i];
  375. end
  376. else
  377. begin
  378. topNormal := lastNormals^[i];
  379. bottomNormal := normal;
  380. end;
  381. lastNormals^[i] := normal;
  382. end;
  383. end;
  384. begin
  385. // to invert normals, we just need to flip top & bottom
  386. if invertNormals then
  387. begin
  388. ptBuffer := ptTop;
  389. ptTop := ptBottom;
  390. ptBottom := ptBuffer;
  391. end;
  392. // generate triangle strip for a level
  393. // TODO : support for triangle fans (when ptTop or ptBottom is on the Y Axis)
  394. alpha := startAlpha;
  395. i := 0;
  396. yOffset := startYOffset;
  397. topTPBase.S := 0;
  398. bottomTPBase.S := 0;
  399. topTPBase.T := topT;
  400. bottomTPBase.T := bottomT;
  401. VectorRotateAroundY(ptTop^, alpha, topBase);
  402. VectorRotateAroundY(ptBottom^, alpha, bottomBase);
  403. if gotYDeltaOffset then
  404. begin
  405. topBase.Y := topBase.Y + yOffset;
  406. bottomBase.Y := bottomBase.Y + yOffset;
  407. yOffset := yOffset + deltaYOffset;
  408. end;
  409. CalcNormal(@topBase, @bottomBase, normal);
  410. SetLocalNormals;
  411. inc(i);
  412. topTPNext := topTPBase;
  413. bottomTPNext := bottomTPBase;
  414. gl.Begin_(GL_TRIANGLE_STRIP);
  415. gl.Normal3fv(@topNormal);
  416. xgl.TexCoord2fv(@topTPBase);
  417. gl.Vertex3fv(@topBase);
  418. while alpha < stopAlpha do
  419. begin
  420. gl.Normal3fv(@bottomNormal);
  421. xgl.TexCoord2fv(@bottomTPBase);
  422. gl.Vertex3fv(@bottomBase);
  423. nextAlpha := alpha + deltaAlpha;
  424. topTPNext.S := topTPNext.S + deltaS;
  425. bottomTPNext.S := bottomTPNext.S + deltaS;
  426. VectorRotateAroundY(ptTop^, nextAlpha, topNext);
  427. VectorRotateAroundY(ptBottom^, nextAlpha, bottomNext);
  428. if gotYDeltaOffset then
  429. begin
  430. topNext.Y := topNext.Y + yOffset;
  431. bottomNext.Y := bottomNext.Y + yOffset;
  432. yOffset := yOffset + deltaYOffset
  433. end;
  434. CalcNormal(@topNext, @bottomNext, normal);
  435. SetLocalNormals;
  436. inc(i);
  437. xgl.TexCoord2fv(@topTPNext);
  438. gl.Normal3fv(@topNormal);
  439. gl.Vertex3fv(@topNext);
  440. alpha := nextAlpha;
  441. topBase := topNext;
  442. topTPBase := topTPNext;
  443. bottomBase := bottomNext;
  444. bottomTPBase := bottomTPNext;
  445. end;
  446. gl.Normal3fv(@bottomNormal);
  447. xgl.TexCoord2fv(@bottomTPBase);
  448. gl.Vertex3fv(@bottomBase);
  449. gl.End_;
  450. firstStep := False;
  451. end;
  452. var
  453. i, nbSteps, nbDivisions: Integer;
  454. splinePos, lastSplinePos, bary, polygonNormal: TAffineVector;
  455. f: Single;
  456. spline: TCubicSpline;
  457. invertedNormals: Boolean;
  458. polygon: TGLNodes;
  459. begin
  460. if (Nodes.Count > 1) and (FStopAngle > FStartAngle) then
  461. begin
  462. startAlpha := FStartAngle * cPIdiv180;
  463. stopAlpha := FStopAngle * cPIdiv180;
  464. nbSteps := Round(((stopAlpha - startAlpha) / (2 * PI)) * FSlices);
  465. // drop 0.1% to slice count to care for precision losses
  466. deltaAlpha := (stopAlpha - startAlpha) / (nbSteps * 0.999);
  467. deltaS := (stopAlpha - startAlpha) / (2 * PI * nbSteps);
  468. gotYDeltaOffset := FYOffsetPerTurn <> 0;
  469. if gotYDeltaOffset then
  470. deltaYOffset := (FYOffsetPerTurn * (stopAlpha - startAlpha) / (2 * PI)) /
  471. nbSteps
  472. else
  473. deltaYOffset := 0;
  474. startYOffset := YOffsetPerTurn * startAlpha / (2 * PI);
  475. invertedNormals := (FNormalDirection = ndInside);
  476. FTriangleCount := 0;
  477. // generate sides
  478. if (rspInside in FParts) or (rspOutside in FParts) then
  479. begin
  480. // allocate lastNormals buffer (if smoothing)
  481. if FNormals = nsSmooth then
  482. begin
  483. GetMem(lastNormals, (FSlices + 2) * SizeOf(TAffineVector));
  484. firstStep := True;
  485. end;
  486. // start working
  487. if rspInside in Parts then
  488. begin
  489. firstStep := True;
  490. if (Division < 2) or (SplineMode = lsmLines) then
  491. begin
  492. // standard line(s), draw directly
  493. for i := 0 to Nodes.Count - 2 do
  494. with Nodes[i] do
  495. begin
  496. BuildStep(PAffineVector(Nodes[i].AsAddress),
  497. PAffineVector(Nodes[i + 1].AsAddress), not invertedNormals,
  498. i / (Nodes.Count - 1), (i + 1) / (Nodes.Count - 1));
  499. end;
  500. FTriangleCount := nbSteps * Nodes.Count * 2;
  501. end
  502. else
  503. begin
  504. // cubic spline
  505. Spline := Nodes.CreateNewCubicSpline;
  506. Spline.SplineAffineVector(0, lastSplinePos);
  507. f := 1 / Division;
  508. nbDivisions := (Nodes.Count - 1) * Division;
  509. for i := 1 to nbDivisions do
  510. begin
  511. Spline.SplineAffineVector(i * f, splinePos);
  512. BuildStep(@lastSplinePos, @splinePos, not invertedNormals,
  513. (i - 1) / nbDivisions, i / nbDivisions);
  514. lastSplinePos := splinePos;
  515. end;
  516. Spline.Free;
  517. FTriangleCount := nbSteps * nbDivisions * 2;
  518. end;
  519. end;
  520. if rspOutside in Parts then
  521. begin
  522. firstStep := True;
  523. if (Division < 2) or (SplineMode = lsmLines) then
  524. begin
  525. // standard line(s), draw directly
  526. for i := 0 to Nodes.Count - 2 do
  527. with Nodes[i] do
  528. begin
  529. BuildStep(PAffineVector(Nodes[i].AsAddress),
  530. PAffineVector(Nodes[i + 1].AsAddress), invertedNormals,
  531. i / (Nodes.Count - 1), (i + 1) / (Nodes.Count - 1));
  532. end;
  533. FTriangleCount := nbSteps * Nodes.Count * 2;
  534. end
  535. else
  536. begin
  537. // cubic spline
  538. Spline := Nodes.CreateNewCubicSpline;
  539. Spline.SplineAffineVector(0, lastSplinePos);
  540. f := 1 / Division;
  541. nbDivisions := (Nodes.Count - 1) * Division;
  542. for i := 1 to nbDivisions do
  543. begin
  544. Spline.SplineAffineVector(i * f, splinePos);
  545. BuildStep(@lastSplinePos, @splinePos, invertedNormals,
  546. (i - 1) / nbDivisions, i / nbDivisions);
  547. lastSplinePos := splinePos;
  548. end;
  549. Spline.Free;
  550. FTriangleCount := nbSteps * nbDivisions * 2;
  551. end;
  552. end;
  553. if (rspInside in FParts) and (rspOutside in FParts) then
  554. FTriangleCount := FTriangleCount * 2;
  555. xgl.TexCoord2fv(@NullTexPoint);
  556. // release lastNormals buffer (if smoothing)
  557. if FNormals = nsSmooth then
  558. FreeMem(lastNormals);
  559. end;
  560. // tessellate start/stop polygons
  561. if (rspStartPolygon in FParts) or (rspStopPolygon in FParts) then
  562. begin
  563. bary := Nodes.Barycenter;
  564. bary.Y := 0;
  565. NormalizeVector(bary);
  566. // tessellate start polygon
  567. if rspStartPolygon in FParts then
  568. begin
  569. polygon := Nodes.CreateCopy(nil);
  570. with polygon do
  571. begin
  572. RotateAroundY(RadToDeg(startAlpha));
  573. Translate(AffineVectorMake(0, startYOffset, 0));
  574. if invertedNormals then
  575. alpha := startAlpha + PI / 2
  576. else
  577. alpha := startAlpha + PI + PI / 2;
  578. polygonNormal := VectorRotateAroundY(bary, alpha);
  579. if SplineMode = lsmLines then
  580. RenderTesselatedPolygon(False, @polygonNormal, 1)
  581. else
  582. RenderTesselatedPolygon(False, @polygonNormal, Division);
  583. Free;
  584. end;
  585. // estimated count
  586. FTriangleCount := FTriangleCount + Nodes.Count + (Nodes.Count shr 1);
  587. end;
  588. // tessellate stop polygon
  589. if rspStopPolygon in FParts then
  590. begin
  591. polygon := Nodes.CreateCopy(nil);
  592. with polygon do
  593. begin
  594. RotateAroundY(RadToDeg(stopAlpha));
  595. Translate(AffineVectorMake(0, startYOffset + (stopAlpha - startAlpha)
  596. * YOffsetPerTurn / (2 * PI), 0));
  597. if invertedNormals then
  598. alpha := stopAlpha + PI + PI / 2
  599. else
  600. alpha := stopAlpha + PI / 2;
  601. polygonNormal := VectorRotateAroundY(bary, alpha);
  602. if SplineMode = lsmLines then
  603. RenderTesselatedPolygon(False, @polygonNormal, 1)
  604. else
  605. RenderTesselatedPolygon(False, @polygonNormal, Division);
  606. Free;
  607. end;
  608. // estimated count
  609. FTriangleCount := FTriangleCount + Nodes.Count + (Nodes.Count shr 1);
  610. end;
  611. end;
  612. end;
  613. end;
  614. function TGLRevolutionSolid.AxisAlignedDimensionsUnscaled: TGLVector;
  615. var
  616. maxRadius: Single;
  617. maxHeight: Single;
  618. i: integer;
  619. begin
  620. maxRadius := 0;
  621. maxHeight := 0;
  622. if FAxisAlignedDimensionsCache.X < 0 then
  623. begin
  624. for i := 0 to Nodes.Count - 1 do
  625. begin
  626. maxHeight := MaxFloat(maxHeight, Abs(Nodes[i].Y));
  627. maxRadius := MaxFloat(maxRadius, Sqr(Nodes[i].X) + Sqr(Nodes[i].Z));
  628. end;
  629. maxRadius := sqrt(maxRadius);
  630. FAxisAlignedDimensionsCache.X := maxRadius;
  631. FAxisAlignedDimensionsCache.Y := maxHeight;
  632. FAxisAlignedDimensionsCache.Z := maxRadius;
  633. end;
  634. SetVector(Result, FAxisAlignedDimensionsCache);
  635. end;
  636. procedure TGLRevolutionSolid.StructureChanged;
  637. begin
  638. FAxisAlignedDimensionsCache.X := -1;
  639. inherited;
  640. end;
  641. // ------------------
  642. // ------------------ TGLPipeNode ------------------
  643. // ------------------
  644. constructor TGLPipeNode.Create(Collection: TCollection);
  645. begin
  646. inherited Create(Collection);
  647. FRadiusFactor := 1.0;
  648. FColor := TGLColor.CreateInitialized(Self, clrBlack, ColorChanged);
  649. FTexCoordT := 1.0;
  650. end;
  651. destructor TGLPipeNode.Destroy;
  652. begin
  653. FColor.Free;
  654. inherited Destroy;
  655. end;
  656. procedure TGLPipeNode.Assign(Source: TPersistent);
  657. begin
  658. if Source is TGLPipeNode then
  659. begin
  660. RadiusFactor := TGLPipeNode(Source).FRadiusFactor;
  661. Color.DirectColor := TGLPipeNode(Source).Color.DirectColor;
  662. TexCoordT := TGLPipeNode(Source).FTexCoordT;
  663. end;
  664. inherited;
  665. end;
  666. function TGLPipeNode.GetDisplayName: string;
  667. begin
  668. Result := Format('%s / rf = %.3f', [inherited GetDisplayName, RadiusFactor]);
  669. ;
  670. end;
  671. procedure TGLPipeNode.SetRadiusFactor(const val: Single);
  672. begin
  673. if FRadiusFactor <> val then
  674. begin
  675. FRadiusFactor := val;
  676. Changed(false);
  677. //(Collection as TGLNodes).NotifyChange;
  678. end;
  679. end;
  680. function TGLPipeNode.StoreRadiusFactor: Boolean;
  681. begin
  682. Result := (FRadiusFactor <> 1.0);
  683. end;
  684. function TGLPipeNode.StoreTexCoordT: Boolean;
  685. begin
  686. Result := (FTexCoordT <> 1.0);
  687. end;
  688. procedure TGLPipeNode.SetColor(const val: TGLColor);
  689. begin
  690. FColor.Assign(val);
  691. end;
  692. procedure TGLPipeNode.ColorChanged(sender: TObject);
  693. begin
  694. TGLPipeNodes(Collection).NotifyChange;
  695. end;
  696. // ------------------
  697. // ------------------ TGLPipeNodes ------------------
  698. // ------------------
  699. constructor TGLPipeNodes.Create(AOwner: TComponent);
  700. begin
  701. inherited Create(AOwner, TGLPipeNode);
  702. end;
  703. procedure TGLPipeNodes.SetItems(index: Integer; const val: TGLPipeNode);
  704. begin
  705. inherited Items[index] := val;
  706. end;
  707. function TGLPipeNodes.GetItems(index: Integer): TGLPipeNode;
  708. begin
  709. Result := TGLPipeNode(inherited Items[index]);
  710. end;
  711. function TGLPipeNodes.Add: TGLPipeNode;
  712. begin
  713. Result := (inherited Add) as TGLPipeNode;
  714. end;
  715. function TGLPipeNodes.FindItemID(ID: Integer): TGLPipeNode;
  716. begin
  717. Result := (inherited FindItemID(ID)) as TGLPipeNode;
  718. end;
  719. // ------------------
  720. // ------------------ TGLPipe ------------------
  721. // ------------------
  722. constructor TGLPipe.Create(AOwner: TComponent);
  723. begin
  724. inherited Create(AOwner);
  725. FSlices := 16;
  726. FParts := [ppOutside];
  727. FRadius := 1.0;
  728. FTriangleCount := 0;
  729. FTextCoordMode := ptcmDefault;
  730. FTextCoordTileS := 1;
  731. FTextCoordTileT := 1;
  732. FNormalMode := pnmDefault;
  733. FNormalSmoothAngle := 0;
  734. end;
  735. procedure TGLPipe.CreateNodes;
  736. begin
  737. FNodes := TGLPipeNodes.Create(Self);
  738. end;
  739. destructor TGLPipe.Destroy;
  740. begin
  741. inherited Destroy;
  742. end;
  743. procedure TGLPipe.SetSlices(const val: Integer);
  744. begin
  745. if (val <> FSlices) and (val > 0) then
  746. begin
  747. FSlices := val;
  748. StructureChanged;
  749. end;
  750. end;
  751. procedure TGLPipe.SetParts(const val: TPipeParts);
  752. begin
  753. if FParts <> val then
  754. begin
  755. FParts := val;
  756. StructureChanged;
  757. end;
  758. end;
  759. procedure TGLPipe.SetRadius(const val: Single);
  760. begin
  761. if FRadius <> val then
  762. begin
  763. FRadius := val;
  764. StructureChanged;
  765. end;
  766. end;
  767. function TGLPipe.StoreRadius: Boolean;
  768. begin
  769. Result := (FRadius <> 1.0);
  770. end;
  771. function TGLPipe.StoreTextCoordTileS: Boolean;
  772. begin
  773. Result := (FTextCoordTileS <> 1.0);
  774. end;
  775. function TGLPipe.StoreTextCoordTileT: Boolean;
  776. begin
  777. Result := (FTextCoordTileT <> 1.0);
  778. end;
  779. procedure TGLPipe.SetNodesColorMode(const val: TPipeNodesColorMode);
  780. begin
  781. if val <> FNodesColorMode then
  782. begin
  783. FNodesColorMode := val;
  784. StructureChanged;
  785. end;
  786. end;
  787. procedure TGLPipe.SetTextCoordMode(const val: TPipeTexCoordMode);
  788. begin
  789. if val <> FTextCoordMode then
  790. begin
  791. FTextCoordMode := val;
  792. StructureChanged;
  793. end;
  794. end;
  795. procedure TGLPipe.SetTextCoordTileS(const val: Single);
  796. begin
  797. if val <> FTextCoordTileS then
  798. begin
  799. FTextCoordTileS := val;
  800. StructureChanged;
  801. end;
  802. end;
  803. procedure TGLPipe.SetTextCoordTileT(const val: Single);
  804. begin
  805. if val <> FTextCoordTileT then
  806. begin
  807. FTextCoordTileT := val;
  808. StructureChanged;
  809. end;
  810. end;
  811. procedure TGLPipe.SetNormalMode(const val: TPipeNormalMode);
  812. begin
  813. if val <> FNormalMode then
  814. begin
  815. FNormalMode := val;
  816. StructureChanged;
  817. end;
  818. end;
  819. procedure TGLPipe.SetNormalSmoothAngle(const val: Single);
  820. begin
  821. if val <> FNormalSmoothAngle then
  822. begin
  823. FNormalSmoothAngle := val;
  824. if NormalMode = pnmAdvanced then
  825. StructureChanged;
  826. end;
  827. end;
  828. procedure TGLPipe.Assign(Source: TPersistent);
  829. begin
  830. if Source is TGLPipe then
  831. begin
  832. Slices := TGLPipe(Source).Slices;
  833. Parts := TGLPipe(Source).Parts;
  834. Radius := TGLPipe(Source).Radius;
  835. NodesColorMode := TGLPipe(Source).NodesColorMode;
  836. TexCoordMode := TGLPipe(Source).TexCoordMode;
  837. TexCoordTileS := TGLPipe(Source).TexCoordTileS;
  838. TexCoordTileT := TGLPipe(Source).TexCoordTileT;
  839. end;
  840. inherited;
  841. end;
  842. var
  843. vSinCache, vCosCache: array of Single;
  844. procedure TGLPipe.BuildList(var rci: TGLRenderContextInfo);
  845. type
  846. TNodeData = record
  847. pos: TAffineVector;
  848. normal: TAffineVector;
  849. innormal: TAffineVector;
  850. sidedir: TVector3f;
  851. end;
  852. TRowData = record
  853. node: array of TNodeData;
  854. color: TGLColorVector;
  855. center: TVector3f;
  856. textcoordT: Single;
  857. end;
  858. PRowData = ^TRowData;
  859. const
  860. cPNCMtoEnum: array[pncmEmission..pncmAmbientAndDiffuse] of Cardinal =
  861. (GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_AMBIENT_AND_DIFFUSE);
  862. procedure CalculateRow(row: PRowData;
  863. const center, normal: TAffineVector; radius: Single);
  864. var
  865. i: Integer;
  866. vx, vy: TAffineVector;
  867. begin
  868. // attempt to use object's Z as Y vector
  869. VectorCrossProduct(ZVector, normal, vx);
  870. if VectorNorm(vx) < 1e-7 then
  871. begin
  872. // bad luck, the X vector will do (unless it's or normal that was null)
  873. if VectorNorm(normal) < 1e-7 then
  874. begin
  875. SetVector(vx, XVector);
  876. SetVector(vy, ZVector);
  877. end
  878. else
  879. begin
  880. VectorCrossProduct(XVector, normal, vx);
  881. NormalizeVector(vx);
  882. VectorCrossProduct(normal, vx, vy);
  883. end;
  884. end
  885. else
  886. begin
  887. NormalizeVector(vx);
  888. VectorCrossProduct(normal, vx, vy);
  889. end;
  890. NormalizeVector(vy);
  891. ScaleVector(vx, FRadius);
  892. ScaleVector(vy, FRadius);
  893. // generate the circle
  894. for i := 0 to High(row^.node) do
  895. begin
  896. row^.node[i].normal := VectorCombine(vx, vy, vCosCache[i], vSinCache[i]);
  897. row^.node[i].pos := VectorCombine(PAffineVector(@center)^,
  898. row^.node[i].normal, 1, radius);
  899. SetVector(row^.node[i].sidedir, 0, 0, 0);
  900. end;
  901. row^.center := center;
  902. end;
  903. procedure RenderDisk(row: PRowData;
  904. const center: TGLVector; const normal: TAffineVector;
  905. invert: Boolean; TextCoordTileS: Single);
  906. var
  907. i: Integer;
  908. begin
  909. begin
  910. if NodesColorMode <> pncmNone then
  911. gl.Color4fv(@row^.color);
  912. // it was necessary to change build process to generate textcoords
  913. gl.Begin_(GL_TRIANGLE_STRIP);
  914. gl.Normal3fv(@normal);
  915. case TexCoordMode of
  916. ptcmDefault, ptcmManual:
  917. begin
  918. if invert then
  919. begin
  920. for i := 0 to High(row^.node) - 1 do
  921. begin
  922. gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 1);
  923. gl.Vertex3fv(@row^.node[i].pos);
  924. gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 0);
  925. gl.Vertex3fv(@center);
  926. end;
  927. gl.TexCoord2f(TextCoordTileS, 1);
  928. gl.Vertex3fv(@row^.node[High(row^.node)].pos);
  929. end
  930. else
  931. begin
  932. for i := High(row^.node) downto 1 do
  933. begin
  934. gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 0);
  935. gl.Vertex3fv(@row^.node[i].pos);
  936. gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 1);
  937. gl.Vertex3fv(@center);
  938. end;
  939. gl.TexCoord2f(0, 0);
  940. gl.Vertex3fv(@row^.node[0].pos);
  941. end;
  942. end;
  943. end;
  944. gl.End_;
  945. end;
  946. end;
  947. procedure CalculateSides(prevRow, curRow: PRowData; const trajvec: TVector3f);
  948. var
  949. j, k, m, n: Integer;
  950. deltaNormal, deltaPos: array of Double;
  951. smoothanglerad: Single;
  952. begin
  953. SetLength(deltanormal, Slices);
  954. SetLength(deltapos, Slices);
  955. for k := 0 to Slices - 1 do
  956. begin //rotate index for curRow
  957. deltanormal[k] := 0; //sum of difference for normal vector
  958. deltapos[k] := 0; //sum of difference for pos vector
  959. for j := 0 to Slices - 1 do
  960. begin //over all places
  961. n := (j + k) mod Slices;
  962. deltanormal[k] := deltanormal[k] + VectorSpacing(curRow^.node[n].normal,
  963. prevRow^.node[j].normal);
  964. deltapos[k] := deltapos[k] + VectorSpacing(curRow^.node[n].pos,
  965. prevRow^.node[j].pos);
  966. end;
  967. end;
  968. //Search minimum
  969. // only search in deltapos, if i would search in deltanormal,
  970. // the same index of minimum would be found
  971. m := 0;
  972. for k := 1 to Slices - 1 do
  973. if deltapos[m] > deltapos[k] then
  974. m := k;
  975. // rotate count
  976. for k := 1 to m do
  977. begin
  978. // rotate the values of curRow
  979. curRow^.node[Slices] := curRow^.node[0];
  980. System.Move(curRow^.node[1], curRow^.node[0], SizeOf(TNodeData) * Slices);
  981. curRow^.node[Slices] := curRow^.node[0];
  982. end;
  983. case NormalMode of
  984. pnmDefault:
  985. begin
  986. for j := 0 to Slices do
  987. begin
  988. curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
  989. prevRow.node[j].innormal := VectorNegate(prevRow.node[j].normal);
  990. end;
  991. end;
  992. pnmAdvanced:
  993. begin
  994. smoothanglerad := DegToRadian(NormalSmoothAngle);
  995. for j := 0 to Slices do
  996. begin
  997. curRow.node[j].sidedir :=
  998. VectorNormalize(VectorSubtract(curRow.node[j].pos,
  999. prevRow.node[j].pos));
  1000. if VectorDotProduct(curRow.node[j].sidedir, prevRow.node[j].sidedir)
  1001. < Cos(smoothanglerad) then
  1002. begin
  1003. if VectorDotProduct(curRow.node[j].sidedir, VectorNormalize(
  1004. VectorSubtract(curRow.node[j].pos, curRow.center))) > 0.99 then
  1005. begin
  1006. curRow.node[j].normal :=
  1007. VectorCrossProduct(curRow.node[j].sidedir,
  1008. VectorCrossProduct(curRow.node[j].sidedir,
  1009. VectorNormalize(trajvec)));
  1010. prevRow.node[j].normal :=
  1011. VectorCrossProduct(curRow.node[j].sidedir,
  1012. VectorCrossProduct(curRow.node[j].sidedir,
  1013. VectorNormalize(trajvec)));
  1014. end
  1015. else
  1016. begin
  1017. if VectorDotProduct(curRow.node[j].sidedir, VectorNormalize(
  1018. VectorSubtract(curRow.node[j].pos, curRow.center))) < -0.99
  1019. then
  1020. begin
  1021. curRow.node[j].normal := VectorCrossProduct(VectorCrossProduct
  1022. (curRow.node[j].sidedir, VectorNormalize(trajvec)),
  1023. curRow.node[j].sidedir);
  1024. prevRow.node[j].normal := VectorCrossProduct(VectorCrossProduct
  1025. (curRow.node[j].sidedir, VectorNormalize(trajvec)),
  1026. curRow.node[j].sidedir);
  1027. end
  1028. else
  1029. begin
  1030. if VectorDotProduct(trajvec, curRow.node[j].sidedir) < 0 then
  1031. begin
  1032. curRow.node[j].normal :=
  1033. VectorCrossProduct(VectorNormalize(VectorCrossProduct
  1034. (VectorNormalize(VectorSubtract(curRow.node[j].pos,
  1035. curRow.center)),
  1036. curRow.node[j].sidedir)), curRow.node[j].sidedir);
  1037. prevRow.node[j].normal :=
  1038. VectorCrossProduct(VectorNormalize(VectorCrossProduct
  1039. (VectorNormalize(VectorSubtract(prevRow.node[j].pos,
  1040. prevRow.center)),
  1041. curRow.node[j].sidedir)), curRow.node[j].sidedir);
  1042. end
  1043. else
  1044. begin
  1045. curRow.node[j].normal :=
  1046. VectorCrossProduct(curRow.node[j].sidedir, VectorNormalize
  1047. (VectorCrossProduct(VectorNormalize(VectorSubtract(curRow.node[j].pos, curRow.center)),
  1048. curRow.node[j].sidedir)));
  1049. prevRow.node[j].normal :=
  1050. VectorCrossProduct(curRow.node[j].sidedir, VectorNormalize
  1051. (VectorCrossProduct(VectorNormalize(VectorSubtract(prevRow.node[j].pos, prevRow.center)),
  1052. curRow.node[j].sidedir)));
  1053. end;
  1054. end;
  1055. if VectorLength(curRow.node[j].normal) = 0 then
  1056. curRow.node[j].normal := prevRow.node[j].normal;
  1057. if VectorLength(prevRow.node[j].normal) = 0 then
  1058. prevRow.node[j].normal := curRow.node[j].normal;
  1059. //compute inside normales
  1060. curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
  1061. prevRow.node[j].innormal :=
  1062. VectorNegate(prevRow.node[j].normal);
  1063. end;
  1064. end
  1065. else
  1066. begin
  1067. if VectorDotProduct(curRow.node[j].sidedir,
  1068. VectorNormalize(VectorSubtract
  1069. (curRow.node[j].pos, curRow.center))) > 0.99 then
  1070. begin
  1071. curRow.node[j].normal :=
  1072. VectorCrossProduct(curRow.node[j].sidedir,
  1073. VectorCrossProduct(curRow.node[j].sidedir,
  1074. VectorNormalize(trajvec)));
  1075. end
  1076. else
  1077. begin
  1078. if VectorDotProduct(curRow.node[j].sidedir, VectorNormalize(
  1079. VectorSubtract(curRow.node[j].pos, curRow.center))) < -0.99
  1080. then
  1081. begin
  1082. curRow.node[j].normal := VectorCrossProduct(VectorCrossProduct
  1083. (curRow.node[j].sidedir, VectorNormalize(trajvec)),
  1084. curRow.node[j].sidedir);
  1085. end
  1086. else
  1087. begin
  1088. if VectorDotProduct(trajvec, curRow.node[j].sidedir) < 0 then
  1089. begin
  1090. curRow.node[j].normal :=
  1091. VectorCrossProduct(VectorNormalize(VectorCrossProduct
  1092. (VectorNormalize(VectorSubtract(curRow.node[j].pos,
  1093. curRow.center)),
  1094. curRow.node[j].sidedir)), curRow.node[j].sidedir);
  1095. end
  1096. else
  1097. begin
  1098. curRow.node[j].normal :=
  1099. VectorCrossProduct(curRow.node[j].sidedir, VectorNormalize
  1100. (VectorCrossProduct(VectorNormalize(VectorSubtract(curRow.node[j].pos,
  1101. curRow.center)), curRow.node[j].sidedir)));
  1102. end;
  1103. end;
  1104. //compute inside normales
  1105. curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
  1106. end;
  1107. end;
  1108. end;
  1109. end;
  1110. end;
  1111. end;
  1112. procedure RenderSides(prevRow, curRow: PRowData; TextCoordTileS,
  1113. TextCoordTileT: Single; outside: Boolean);
  1114. var
  1115. j: Integer;
  1116. begin
  1117. begin
  1118. gl.Begin_(GL_TRIANGLE_STRIP);
  1119. if outside then
  1120. begin
  1121. if NodesColorMode <> pncmNone then
  1122. gl.Color4fv(@curRow^.color);
  1123. gl.TexCoord2f(0, curRow^.textcoordT * TextCoordTileT);
  1124. gl.Normal3fv(@curRow^.node[0].normal);
  1125. gl.Vertex3fv(@curRow^.node[0].pos);
  1126. for j := 0 to Slices - 1 do
  1127. begin
  1128. if NodesColorMode <> pncmNone then
  1129. gl.Color4fv(@prevRow^.color);
  1130. gl.TexCoord2f(j / Slices * TextCoordTileS, prevRow^.textcoordT *
  1131. TextCoordTileT);
  1132. gl.Normal3fv(@prevRow^.node[j].normal);
  1133. gl.Vertex3fv(@prevRow^.node[j].pos);
  1134. if NodesColorMode <> pncmNone then
  1135. gl.Color4fv(@curRow^.color);
  1136. gl.TexCoord2f((j + 1) / Slices * TextCoordTileS, curRow^.textcoordT *
  1137. TextCoordTileT);
  1138. gl.Normal3fv(@curRow^.node[j + 1].normal);
  1139. gl.Vertex3fv(@curRow^.node[j + 1].pos);
  1140. end;
  1141. if NodesColorMode <> pncmNone then
  1142. gl.Color4fv(@prevRow^.color);
  1143. gl.TexCoord2f(TextCoordTileS, prevRow^.textcoordT * TextCoordTileT);
  1144. gl.Normal3fv(@prevRow^.node[Slices].normal);
  1145. gl.Vertex3fv(@prevRow^.node[Slices].pos);
  1146. end
  1147. else
  1148. begin
  1149. for j := 0 to Slices do
  1150. begin
  1151. curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
  1152. prevRow.node[j].innormal := VectorNegate(prevRow.node[j].normal);
  1153. end;
  1154. if NodesColorMode <> pncmNone then
  1155. gl.Color4fv(@prevRow^.color);
  1156. gl.TexCoord2f(0, prevRow^.textcoordT * TextCoordTileT);
  1157. gl.Normal3fv(@prevRow^.node[0].innormal);
  1158. gl.Vertex3fv(@prevRow^.node[0].pos);
  1159. for j := 0 to Slices - 1 do
  1160. begin
  1161. if NodesColorMode <> pncmNone then
  1162. gl.Color4fv(@curRow^.color);
  1163. gl.TexCoord2f(j / Slices * TextCoordTileS, curRow^.textcoordT *
  1164. TextCoordTileT);
  1165. gl.Normal3fv(@curRow^.node[j].innormal);
  1166. gl.Vertex3fv(@curRow^.node[j].pos);
  1167. if NodesColorMode <> pncmNone then
  1168. gl.Color4fv(@prevRow^.color);
  1169. gl.TexCoord2f((j + 1) / Slices * TextCoordTileS, prevRow^.textcoordT *
  1170. TextCoordTileT);
  1171. gl.Normal3fv(@prevRow^.node[j + 1].innormal);
  1172. gl.Vertex3fv(@prevRow^.node[j + 1].pos);
  1173. end;
  1174. if NodesColorMode <> pncmNone then
  1175. gl.Color4fv(@curRow^.color);
  1176. gl.TexCoord2f(TextCoordTileS, curRow^.textcoordT * TextCoordTileT);
  1177. gl.Normal3fv(@curRow^.node[Slices].innormal);
  1178. gl.Vertex3fv(@curRow^.node[Slices].pos);
  1179. end;
  1180. gl.End_;
  1181. end;
  1182. end;
  1183. var
  1184. i, curRow, nbDivisions, k: Integer;
  1185. normal, splinePos: TAffineVector;
  1186. rows: array[0..1] of TRowData;
  1187. ra: PFloatArray;
  1188. posSpline, rSpline: TCubicSpline;
  1189. f, t: Single;
  1190. begin
  1191. FTriangleCount := 0;
  1192. if Nodes.Count = 0 then
  1193. Exit;
  1194. SetLength(rows[0].node, Slices + 1);
  1195. SetLength(rows[1].node, Slices + 1);
  1196. if (Length(vSinCache) <> Slices + 1) or (Length(vCosCache) <> Slices + 1) then
  1197. begin
  1198. SetLength(vSinCache, Slices + 1);
  1199. SetLength(vCosCache, Slices + 1);
  1200. PrepareSinCosCache(vSinCache, vCosCache, 0, 360);
  1201. end;
  1202. if (SplineMode = lsmCubicSpline) and (Nodes.Count > 1) then
  1203. begin
  1204. // creates position spline
  1205. posSpline := Nodes.CreateNewCubicSpline;
  1206. // creates radius spline
  1207. GetMem(ra, SizeOf(TGLFloat) * Nodes.Count);
  1208. for i := 0 to Nodes.Count - 1 do
  1209. ra^[i] := TGLPipeNode(Nodes[i]).RadiusFactor;
  1210. rSpline := TCubicSpline.Create(ra, nil, nil, nil, Nodes.Count);
  1211. FreeMem(ra);
  1212. normal := posSpline.SplineSlopeVector(0);
  1213. end
  1214. else
  1215. begin
  1216. normal := Nodes.Vector(0);
  1217. posSpline := nil;
  1218. rSpline := nil;
  1219. end;
  1220. if NodesColorMode <> pncmNone then
  1221. begin
  1222. gl.ColorMaterial(GL_FRONT_AND_BACK, cPNCMtoEnum[NodesColorMode]);
  1223. rci.GLStates.Enable(stColorMaterial);
  1224. end
  1225. else
  1226. rci.GLStates.Disable(stColorMaterial);
  1227. CalculateRow(@rows[0], PAffineVector(@Nodes[0].AsVector)^, normal,
  1228. TGLPipeNode(Nodes[0]).RadiusFactor);
  1229. rows[0].color := TGLPipeNodes(Nodes)[0].Color.Color;
  1230. case TexCoordMode of
  1231. ptcmDefault: rows[0].textcoordT := 0;
  1232. ptcmManual: rows[0].textcoordT := TGLPipeNode(Nodes[0]).TexCoordT;
  1233. end;
  1234. if ppStartDisk in Parts then
  1235. begin
  1236. NegateVector(normal);
  1237. if ppOutside in Parts then
  1238. begin
  1239. RenderDisk(@rows[0], Nodes[0].AsVector, normal, True, TexCoordTileS);
  1240. FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
  1241. end;
  1242. if ppInside in Parts then
  1243. begin
  1244. RenderDisk(@rows[0], Nodes[0].AsVector, VectorNegate(normal), False,
  1245. TexCoordTileS);
  1246. FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
  1247. end;
  1248. end;
  1249. if (Nodes.Count > 1) then
  1250. begin
  1251. if SplineMode = lsmCubicSpline then
  1252. begin
  1253. f := 1 / Division;
  1254. nbDivisions := (Nodes.Count - 1) * Division;
  1255. for i := 1 to nbDivisions do
  1256. begin
  1257. t := i * f;
  1258. posSpline.SplineAffineVector(t, splinePos);
  1259. normal := posSpline.SplineSlopeVector(t);
  1260. NormalizeVector(normal);
  1261. curRow := (i and 1);
  1262. CalculateRow(@rows[curRow], splinePos, normal,
  1263. rSpline.SplineX(t));
  1264. if NodesColorMode <> pncmNone then
  1265. begin
  1266. k := Trunc(t);
  1267. if k < Nodes.Count - 1 then
  1268. rows[curRow].color := VectorLerp(TGLPipeNodes(Nodes)[k].Color.Color,
  1269. TGLPipeNodes(Nodes)[k + 1].Color.Color,
  1270. Frac(t))
  1271. else
  1272. rows[curRow].color := TGLPipeNodes(Nodes)[k].Color.Color;
  1273. end;
  1274. //
  1275. case TexCoordMode of
  1276. ptcmDefault:
  1277. begin
  1278. k := Trunc(t);
  1279. if k < Nodes.Count - 1 then
  1280. rows[curRow].textcoordT := Lerp(k,
  1281. k + 1,
  1282. Frac(t))
  1283. else
  1284. rows[curRow].textcoordT := k;
  1285. end;
  1286. ptcmManual:
  1287. begin
  1288. k := Trunc(t);
  1289. if k < Nodes.Count - 1 then
  1290. rows[curRow].textcoordT := Lerp(TGLPipeNode(Nodes[k]).TexCoordT,
  1291. TGLPipeNode(Nodes[k + 1]).TexCoordT,
  1292. Frac(t))
  1293. else
  1294. rows[curRow].textcoordT := TGLPipeNode(Nodes[k]).TexCoordT;
  1295. end;
  1296. end;
  1297. if (ppOutside in Parts) or (ppInside in Parts) then
  1298. CalculateSides(@rows[curRow xor 1], @rows[curRow], normal);
  1299. if ppOutside in Parts then
  1300. RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
  1301. TexCoordTileT, True);
  1302. if ppInside in Parts then
  1303. RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
  1304. TexCoordTileT, False);
  1305. end;
  1306. i := nbDivisions * (Slices + 1) * 2;
  1307. if ppOutside in Parts then
  1308. Inc(FTriangleCount, i);
  1309. if ppInside in Parts then
  1310. Inc(FTriangleCount, i);
  1311. end
  1312. else
  1313. begin
  1314. for i := 1 to Nodes.Count - 1 do
  1315. begin
  1316. curRow := (i and 1);
  1317. //Initialize Texture coordinates
  1318. case TexCoordMode of
  1319. ptcmDefault: rows[curRow].textcoordT := i;
  1320. ptcmManual: rows[curRow].textcoordT :=
  1321. TGLPipeNode(Nodes[i]).TexCoordT;
  1322. end;
  1323. CalculateRow(@rows[curRow], PAffineVector(@Nodes[i].AsVector)^,
  1324. Nodes.Vector(i), TGLPipeNode(Nodes[i]).RadiusFactor);
  1325. rows[curRow].color := TGLPipeNodes(Nodes)[i].Color.Color;
  1326. if (ppOutside in Parts) or (ppInside in Parts) then
  1327. CalculateSides(@rows[curRow xor 1], @rows[curRow], Nodes.Vector(i));
  1328. if ppOutside in Parts then
  1329. RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
  1330. TexCoordTileT, True);
  1331. if ppInside in Parts then
  1332. RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
  1333. TexCoordTileT, False);
  1334. end;
  1335. i := Nodes.Count * (Slices + 1) * 2;
  1336. if ppOutside in Parts then
  1337. Inc(FTriangleCount, i);
  1338. if ppInside in Parts then
  1339. Inc(FTriangleCount, i);
  1340. end;
  1341. end;
  1342. if ppStopDisk in Parts then
  1343. begin
  1344. i := Nodes.Count - 1;
  1345. if SplineMode = lsmCubicSpline then
  1346. normal := posSpline.SplineSlopeVector(Nodes.Count - 1)
  1347. else
  1348. normal := Nodes.Vector(i);
  1349. CalculateRow(@rows[0], PAffineVector(@Nodes[i].AsVector)^, normal,
  1350. TGLPipeNode(Nodes[i]).RadiusFactor);
  1351. rows[0].color := TGLPipeNodes(Nodes)[i].Color.Color;
  1352. if ppOutside in Parts then
  1353. begin
  1354. RenderDisk(@rows[0], Nodes[i].AsVector, normal, False, TexCoordTileS);
  1355. FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
  1356. end;
  1357. if ppInside in Parts then
  1358. begin
  1359. RenderDisk(@rows[0], Nodes[i].AsVector, VectorNegate(normal), True,
  1360. TexCoordTileS);
  1361. FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
  1362. end;
  1363. end;
  1364. if SplineMode = lsmCubicSpline then
  1365. begin
  1366. posSpline.Free;
  1367. rSpline.Free;
  1368. end;
  1369. end;
  1370. // ------------------
  1371. // ------------------ TGLExtrusionSolid ------------------
  1372. // ------------------
  1373. procedure TGLExtrusionSolid.Assign(Source: TPersistent);
  1374. begin
  1375. if Source is TGLExtrusionSolid then
  1376. begin
  1377. FStacks := TGLExtrusionSolid(Source).FStacks;
  1378. FNormals := TGLExtrusionSolid(Source).FNormals;
  1379. FNormalDirection := TGLExtrusionSolid(Source).FNormalDirection;
  1380. FParts := TGLExtrusionSolid(Source).FParts;
  1381. end;
  1382. inherited;
  1383. end;
  1384. procedure TGLExtrusionSolid.BuildList(var rci: TGLRenderContextInfo);
  1385. var
  1386. {deltaS,}deltaZ: Single;
  1387. lastNormal: TAffineVector;
  1388. procedure CalcNormal(const Top, Bottom: TAffineVector; var normal:
  1389. TAffineVector);
  1390. { extrusion is in Z direction, so the Z component of the normal vector is
  1391. always zero. }
  1392. {var
  1393. p : TAffineVector;}
  1394. begin
  1395. normal.X := Bottom.Y - Top.Y;
  1396. normal.Y := Top.X - Bottom.X;
  1397. normal.Z := 0;
  1398. NormalizeVector(normal);
  1399. if FHeight < 0 then
  1400. NegateVector(normal);
  1401. (*
  1402. p:=Top; p[2]:=p[2] + FHeight;
  1403. CalcPlaneNormal(top,bottom,p,normal);
  1404. *)
  1405. end;
  1406. procedure BuildStep(ptTop, ptBottom: TAffineVector; invertNormals: Boolean;
  1407. topT, bottomT: Single);
  1408. var
  1409. step: Integer;
  1410. topBase, topNext, bottomBase, bottomNext, normal, normTop, normBottom:
  1411. TAffineVector;
  1412. topTPBase, topTPNext, bottomTPBase, bottomTPNext: TTexPoint;
  1413. ptBuffer: TAffineVector;
  1414. angle: Double;
  1415. dir: TAffineVector;
  1416. begin
  1417. // to invert normals, we just need to flip top & bottom
  1418. if invertNormals then
  1419. begin
  1420. ptBuffer := ptTop;
  1421. ptTop := ptBottom;
  1422. ptBottom := ptBuffer;
  1423. end;
  1424. // generate triangle strip for a level
  1425. // TODO : support for triangle fans (when ptTop or ptBottom is on the Y Axis)
  1426. /// topTPBase.S:=0; bottomTPBase.S:=0;
  1427. topTPBase.T := topT;
  1428. bottomTPBase.T := bottomT;
  1429. topBase := ptTop;
  1430. bottomBase := ptBottom;
  1431. CalcNormal(topBase, bottomBase, normal);
  1432. if (FNormals = nsFlat) then
  1433. lastNormal := normal
  1434. else if (FNormals = nsSmooth) then
  1435. begin
  1436. angle := VectorDotProduct(normal, lastNormal);
  1437. if (angle < FMinSmoothAngleCos) then
  1438. begin
  1439. lastNormal := normal;
  1440. end;
  1441. end;
  1442. if invertNormals then
  1443. begin
  1444. normTop := Normal;
  1445. normBottom := lastnormal;
  1446. end
  1447. else
  1448. begin
  1449. normTop := lastNormal;
  1450. normBottom := normal;
  1451. end;
  1452. dir := VectorNormalize(VectorSubtract(bottomBase, topBase));
  1453. topTPBase.S := VectorDotProduct(topBase, dir);
  1454. topTPBase.T := topBase.Z;
  1455. bottomTPBase.S := VectorDotProduct(bottomBase, dir);
  1456. bottomTPBase.T := bottomBase.Z;
  1457. lastNormal := normal;
  1458. topNext := topBase;
  1459. bottomNext := bottomBase;
  1460. topTPNext := topTPBase;
  1461. bottomTPNext := bottomTPBase;
  1462. gl.Begin_(GL_TRIANGLE_STRIP);
  1463. gl.Normal3fv(@normTop);
  1464. xgl.TexCoord2fv(@topTPBase);
  1465. gl.Vertex3fv(@topBase);
  1466. for step := 1 to FStacks do
  1467. begin
  1468. gl.Normal3fv(@normBottom);
  1469. xgl.TexCoord2fv(@bottomTPBase);
  1470. gl.Vertex3fv(@bottomBase);
  1471. topNext.Z := step * DeltaZ;
  1472. bottomNext.Z := topNext.Z;
  1473. topTPNext.T := topNext.Z;
  1474. bottomTPNext.T := bottomNext.Z;
  1475. xgl.TexCoord2fv(@topTPNext);
  1476. gl.Normal3fv(@normTop);
  1477. gl.Vertex3fv(@topNext);
  1478. topBase := topNext;
  1479. topTPBase := topTPNext;
  1480. bottomBase := bottomNext;
  1481. bottomTPBase := bottomTPNext;
  1482. end;
  1483. gl.Normal3fv(@normBottom);
  1484. xgl.TexCoord2fv(@bottomTPBase);
  1485. gl.Vertex3fv(@bottomBase);
  1486. gl.End_;
  1487. end;
  1488. var
  1489. n, i: Integer;
  1490. invertedNormals: Boolean;
  1491. normal: TAffineVector;
  1492. begin
  1493. if Outline.Count < 1 then
  1494. Exit;
  1495. deltaZ := FHeight / FStacks;
  1496. // deltaS:=1/FStacks;
  1497. invertedNormals := (FNormalDirection = ndInside);
  1498. FTriangleCount := 0;
  1499. // generate sides
  1500. if (FHeight <> 0) and ((espInside in FParts) or (espOutside in FParts)) then
  1501. begin
  1502. for n := 0 to Outline.Count - 1 do
  1503. begin
  1504. with Outline.List[n] do
  1505. if count > 1 then
  1506. begin
  1507. if espInside in Parts then
  1508. begin
  1509. CalcNormal(List^[count - 1], List^[0], lastNormal);
  1510. if not InvertedNormals then
  1511. NegateVector(lastNormal);
  1512. for i := 0 to Count - 2 do
  1513. begin
  1514. BuildStep(List^[i], List^[i + 1], not invertedNormals,
  1515. i / (Count - 1), (i + 1) / (Count - 1));
  1516. end;
  1517. BuildStep(List^[count - 1], List^[0], not invertedNormals, 1, 0);
  1518. end;
  1519. if espOutside in Parts then
  1520. begin
  1521. CalcNormal(List^[count - 1], List^[0], lastNormal);
  1522. if InvertedNormals then
  1523. NegateVector(lastNormal);
  1524. for i := 0 to Count - 2 do
  1525. begin
  1526. BuildStep(List^[i], List^[i + 1], invertedNormals,
  1527. i / (Count - 1), (i + 1) / (Count - 1));
  1528. end;
  1529. BuildStep(List^[count - 1], List^[0], invertedNormals, 1, 0);
  1530. end;
  1531. end;
  1532. end;
  1533. xgl.TexCoord2fv(@NullTexPoint);
  1534. end;
  1535. // tessellate start/stop polygons
  1536. if (espStartPolygon in FParts) or (espStopPolygon in FParts) then
  1537. begin
  1538. normal := ContoursNormal;
  1539. // tessellate stop polygon
  1540. if espStopPolygon in FParts then
  1541. begin
  1542. gl.PushMatrix;
  1543. gl.Translatef(0, 0, FHeight);
  1544. RenderTesselatedPolygon(true, @normal, invertedNormals);
  1545. gl.PopMatrix;
  1546. end;
  1547. // tessellate start polygon
  1548. if espStartPolygon in FParts then
  1549. begin
  1550. NegateVector(normal);
  1551. RenderTesselatedPolygon(true, @normal, not invertedNormals);
  1552. end;
  1553. end;
  1554. end;
  1555. constructor TGLExtrusionSolid.Create(AOwner: TComponent);
  1556. begin
  1557. inherited;
  1558. FHeight := 1;
  1559. FStacks := 1;
  1560. FNormals := nsFlat;
  1561. FNormalDirection := ndOutside;
  1562. FParts := [espOutside];
  1563. MinSmoothAngle := 5;
  1564. FAxisAlignedDimensionsCache.X := -1;
  1565. end;
  1566. destructor TGLExtrusionSolid.Destroy;
  1567. begin
  1568. inherited;
  1569. end;
  1570. procedure TGLExtrusionSolid.SetHeight(const Value: TGLFloat);
  1571. begin
  1572. if (Value <> FHeight) then
  1573. begin
  1574. FHeight := Value;
  1575. StructureChanged;
  1576. end;
  1577. end;
  1578. procedure TGLExtrusionSolid.SetMinSmoothAngle(const Value: Single);
  1579. var
  1580. s, c: Single;
  1581. begin
  1582. FMinSmoothAngle := Value;
  1583. SinCosine(Value * cPidiv180, s, c);
  1584. FMinSmoothAngleCos := c;
  1585. end;
  1586. procedure TGLExtrusionSolid.SetNormalDirection(const val: TGLNormalDirection);
  1587. begin
  1588. if FNormalDirection <> val then
  1589. begin
  1590. FNormalDirection := val;
  1591. StructureChanged;
  1592. end;
  1593. end;
  1594. procedure TGLExtrusionSolid.SetNormals(const val: TGLNormalSmoothing);
  1595. begin
  1596. if FNormals <> val then
  1597. begin
  1598. FNormals := val;
  1599. StructureChanged;
  1600. end;
  1601. end;
  1602. procedure TGLExtrusionSolid.SetParts(const val: TGLExtrusionSolidParts);
  1603. begin
  1604. if FParts <> val then
  1605. begin
  1606. FParts := val;
  1607. StructureChanged;
  1608. end;
  1609. end;
  1610. procedure TGLExtrusionSolid.SetStacks(const val: Integer);
  1611. begin
  1612. if (val <> FStacks) and (val > 0) then
  1613. begin
  1614. FStacks := val;
  1615. StructureChanged;
  1616. end;
  1617. end;
  1618. function TGLExtrusionSolid.AxisAlignedDimensionsUnscaled: TGLVector;
  1619. var
  1620. dMin, dMax: TAffineVector;
  1621. begin
  1622. if FAxisAlignedDimensionsCache.X < 0 then
  1623. begin
  1624. Contours.GetExtents(dMin, dMax);
  1625. FAxisAlignedDimensionsCache.X := MaxFloat(Abs(dMin.X), Abs(dMax.X));
  1626. FAxisAlignedDimensionsCache.Y := MaxFloat(Abs(dMin.Y), Abs(dMax.Y));
  1627. FAxisAlignedDimensionsCache.Z := MaxFloat(Abs(dMin.Z), Abs(dMax.Z +
  1628. Height));
  1629. end;
  1630. SetVector(Result, FAxisAlignedDimensionsCache);
  1631. end;
  1632. procedure TGLExtrusionSolid.StructureChanged;
  1633. begin
  1634. FAxisAlignedDimensionsCache.X := -1;
  1635. inherited;
  1636. end;
  1637. // ------------------------------------------------------------------
  1638. initialization
  1639. // ------------------------------------------------------------------
  1640. RegisterClasses([TGLRevolutionSolid, TGLExtrusionSolid, TGLPipe]);
  1641. end.