GLS.Extrusion.pas 54 KB

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