GXS.Extrusion.pas 53 KB

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