GLGeomObjects.pas 99 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. (*
  5. Geometric objects.
  6. *)
  7. unit GLGeomObjects;
  8. interface
  9. {$I GLScene.inc}
  10. uses
  11. System.Math,
  12. System.Classes,
  13. OpenGLTokens,
  14. OpenGLAdapter,
  15. GLScene,
  16. GLPersistentClasses,
  17. GLVectorGeometry,
  18. GLVectorLists,
  19. GLContext,
  20. GLObjects,
  21. GLSilhouette,
  22. GLVectorTypes,
  23. GLGeometryBB,
  24. GLRenderContextInfo,
  25. XOpenGL,
  26. GLPolynomials;
  27. type
  28. (* A Disk object that may not be complete, it can have a hole (controlled by the
  29. InnerRadius property) and can only be a slice (controlled by the StartAngle
  30. and SweepAngle properties). *)
  31. TGLDisk = class(TGLQuadricObject)
  32. private
  33. FStartAngle, FSweepAngle, FOuterRadius, FInnerRadius: Single;
  34. FSlices, FLoops: Integer;
  35. procedure SetOuterRadius(const aValue: Single);
  36. procedure SetInnerRadius(const aValue: Single);
  37. procedure SetSlices(aValue: Integer);
  38. procedure SetLoops(aValue: Integer);
  39. procedure SetStartAngle(const aValue: Single);
  40. procedure SetSweepAngle(const aValue: Single);
  41. public
  42. constructor Create(AOwner: TComponent); override;
  43. procedure BuildList(var rci: TGLRenderContextInfo); override;
  44. procedure Assign(Source: TPersistent); override;
  45. function AxisAlignedDimensionsUnscaled: TVector; override;
  46. function RayCastIntersect(const rayStart, rayVector: TVector;
  47. intersectPoint: PVector = nil; intersectNormal: PVector = nil) : Boolean; override;
  48. published
  49. // Allows defining a "hole" in the disk
  50. property InnerRadius: Single read FInnerRadius write SetInnerRadius;
  51. // Number of radial mesh subdivisions
  52. property Loops: Integer read FLoops write SetLoops default 2;
  53. // Outer radius for the disk. If you leave InnerRadius at 0, this is the disk radius
  54. property OuterRadius: Single read FOuterRadius write SetOuterRadius;
  55. // Number of mesh slices. For instance, if Slices=6, your disk will look like an hexagon
  56. property Slices: Integer read FSlices write SetSlices default 16;
  57. property StartAngle: Single read FStartAngle write SetStartAngle;
  58. property SweepAngle: Single read FSweepAngle write SetSweepAngle;
  59. end;
  60. (* Base class to cylinder-like objects that introduces the basic cylinder description properties.
  61. Be aware teh default slices and stacks make up for a high-poly cylinder,
  62. unless you're after high-quality lighting it is recommended to reduce the
  63. Stacks property to 1. *)
  64. TGLCylinderBase = class(TGLQuadricObject)
  65. private
  66. FBottomRadius: Single;
  67. FSlices, FStacks, FLoops: Integer;
  68. FHeight: Single;
  69. protected
  70. procedure SetBottomRadius(const aValue: Single);
  71. procedure SetHeight(const aValue: Single);
  72. procedure SetSlices(aValue: Integer);
  73. procedure SetStacks(aValue: Integer);
  74. procedure SetLoops(aValue: Integer);
  75. function GetTopRadius: Single; virtual;
  76. public
  77. constructor Create(AOwner: TComponent); override;
  78. procedure Assign(Source: TPersistent); override;
  79. function GenerateSilhouette(const silhouetteParameters
  80. : TGLSilhouetteParameters): TGLSilhouette; override;
  81. published
  82. property BottomRadius: Single read FBottomRadius write SetBottomRadius;
  83. property Height: Single read FHeight write SetHeight;
  84. property Slices: Integer read FSlices write SetSlices default 16;
  85. property Stacks: Integer read FStacks write SetStacks default 4;
  86. // Number of concentric rings for top/bottom disk(s).
  87. property Loops: Integer read FLoops write SetLoops default 1;
  88. end;
  89. TConePart = (coSides, coBottom);
  90. TConeParts = set of TConePart;
  91. // A cone object
  92. TGLCone = class(TGLCylinderBase)
  93. private
  94. FParts: TConeParts;
  95. protected
  96. procedure SetParts(aValue: TConeParts);
  97. function GetTopRadius: Single; override;
  98. public
  99. constructor Create(AOwner: TComponent); override;
  100. procedure Assign(Source: TPersistent); override;
  101. procedure BuildList(var rci: TGLRenderContextInfo); override;
  102. function AxisAlignedDimensionsUnscaled: TVector; override;
  103. function RayCastIntersect(const rayStart, rayVector: TVector;
  104. intersectPoint: PVector = nil; intersectNormal: PVector = nil)
  105. : Boolean; override;
  106. published
  107. property Parts: TConeParts read FParts write SetParts
  108. default [coSides, coBottom];
  109. end;
  110. TCylinderPart = (cySides, cyBottom, cyTop);
  111. TCylinderParts = set of TCylinderPart;
  112. TCylinderAlignment = (caCenter, caTop, caBottom);
  113. // Cylinder object, can also be used to make truncated cones
  114. TGLCylinder = class(TGLCylinderBase)
  115. private
  116. FParts: TCylinderParts;
  117. FTopRadius: Single;
  118. FAlignment: TCylinderAlignment;
  119. protected
  120. procedure SetTopRadius(const aValue: Single);
  121. procedure SetParts(aValue: TCylinderParts);
  122. procedure SetAlignment(val: TCylinderAlignment);
  123. function GetTopRadius: Single; override;
  124. public
  125. constructor Create(AOwner: TComponent); override;
  126. procedure Assign(Source: TPersistent); override;
  127. procedure BuildList(var rci: TGLRenderContextInfo); override;
  128. function AxisAlignedDimensionsUnscaled: TVector; override;
  129. function RayCastIntersect(const rayStart, rayVector: TVector;
  130. intersectPoint: PVector = nil; intersectNormal: PVector = nil)
  131. : Boolean; override;
  132. procedure Align(const startPoint, endPoint: TVector); overload;
  133. procedure Align(const startObj, endObj: TGLBaseSceneObject); overload;
  134. procedure Align(const startPoint, endPoint: TAffineVector); overload;
  135. published
  136. property TopRadius: Single read FTopRadius write SetTopRadius;
  137. property Parts: TCylinderParts read FParts write SetParts
  138. default [cySides, cyBottom, cyTop];
  139. property Alignment: TCylinderAlignment read FAlignment write SetAlignment
  140. default caCenter;
  141. end;
  142. // Capsule object, can also be used to make truncated cones
  143. TGLCapsule = class(TGLSceneObject)
  144. private
  145. FParts: TCylinderParts;
  146. FRadius: Single;
  147. FSlices: Integer;
  148. FStacks: Integer;
  149. FHeight: Single;
  150. FAlignment: TCylinderAlignment;
  151. protected
  152. procedure SetHeight(const aValue: Single);
  153. procedure SetRadius(const aValue: Single);
  154. procedure SetSlices(const aValue: integer);
  155. procedure SetStacks(const aValue: integer);
  156. procedure SetParts(aValue: TCylinderParts);
  157. procedure SetAlignment(val: TCylinderAlignment);
  158. public
  159. constructor Create(AOwner: TComponent); override;
  160. procedure Assign(Source: TPersistent); override;
  161. procedure BuildList(var rci: TGLRenderContextInfo); override;
  162. function AxisAlignedDimensionsUnscaled: TVector; override;
  163. function RayCastIntersect(const rayStart, rayVector: TVector;
  164. intersectPoint: PVector = nil; intersectNormal: PVector = nil)
  165. : Boolean; override;
  166. procedure Align(const startPoint, endPoint: TVector); overload;
  167. procedure Align(const startObj, endObj: TGLBaseSceneObject); overload;
  168. procedure Align(const startPoint, endPoint: TAffineVector); overload;
  169. published
  170. property Height: Single read FHeight write SetHeight;
  171. property Slices: Integer read FSlices write SetSlices;
  172. property Stacks: Integer read FStacks write SetStacks;
  173. property Radius: Single read FRadius write SetRadius;
  174. property Parts: TCylinderParts read FParts write SetParts
  175. default [cySides, cyBottom, cyTop];
  176. property Alignment: TCylinderAlignment read FAlignment write SetAlignment
  177. default caCenter;
  178. end;
  179. TAnnulusPart = (anInnerSides, anOuterSides, anBottom, anTop);
  180. TAnnulusParts = set of TAnnulusPart;
  181. // An annulus is a cylinder that can be made hollow (pipe-like)
  182. TGLAnnulus = class(TGLCylinderBase)
  183. private
  184. FParts: TAnnulusParts;
  185. FBottomInnerRadius: Single;
  186. FTopInnerRadius: Single;
  187. FTopRadius: Single;
  188. protected
  189. procedure SetTopRadius(const aValue: Single);
  190. procedure SetTopInnerRadius(const aValue: Single);
  191. procedure SetBottomInnerRadius(const aValue: Single);
  192. procedure SetParts(aValue: TAnnulusParts);
  193. public
  194. constructor Create(AOwner: TComponent); override;
  195. procedure Assign(Source: TPersistent); override;
  196. procedure BuildList(var rci: TGLRenderContextInfo); override;
  197. function AxisAlignedDimensionsUnscaled: TVector; override;
  198. function RayCastIntersect(const rayStart, rayVector: TVector;
  199. intersectPoint: PVector = nil; intersectNormal: PVector = nil)
  200. : Boolean; override;
  201. published
  202. property BottomInnerRadius: Single read FBottomInnerRadius
  203. write SetBottomInnerRadius;
  204. property TopInnerRadius: Single read FTopInnerRadius
  205. write SetTopInnerRadius;
  206. property TopRadius: Single read FTopRadius write SetTopRadius;
  207. property Parts: TAnnulusParts read FParts write SetParts
  208. default [anInnerSides, anOuterSides, anBottom, anTop];
  209. end;
  210. TTorusPart = (toSides, toStartDisk, toStopDisk);
  211. TTorusParts = set of TTorusPart;
  212. // A Torus object
  213. TGLTorus = class(TGLSceneObject)
  214. private
  215. FParts: TTorusParts;
  216. FRings, FSides: Cardinal;
  217. FStartAngle, FStopAngle: Single;
  218. FMinorRadius, FMajorRadius: Single;
  219. FMesh: array of array of TVertexRec;
  220. protected
  221. procedure SetMajorRadius(const aValue: Single);
  222. procedure SetMinorRadius(const aValue: Single);
  223. procedure SetRings(aValue: Cardinal);
  224. procedure SetSides(aValue: Cardinal);
  225. procedure SetStartAngle(const aValue: Single);
  226. procedure SetStopAngle(const aValue: Single);
  227. procedure SetParts(aValue: TTorusParts);
  228. public
  229. constructor Create(AOwner: TComponent); override;
  230. procedure BuildList(var rci: TGLRenderContextInfo); override;
  231. function AxisAlignedDimensionsUnscaled: TVector; override;
  232. function RayCastIntersect(const rayStart, rayVector: TVector;
  233. intersectPoint: PVector = nil; intersectNormal: PVector = nil): Boolean; override;
  234. published
  235. property MajorRadius: Single read FMajorRadius write SetMajorRadius;
  236. property MinorRadius: Single read FMinorRadius write SetMinorRadius;
  237. property Rings: Cardinal read FRings write SetRings default 25;
  238. property Sides: Cardinal read FSides write SetSides default 15;
  239. property StartAngle: Single read FStartAngle write SetStartAngle;
  240. property StopAngle: Single read FStopAngle write SetStopAngle;
  241. property Parts: TTorusParts read FParts write SetParts default [toSides];
  242. end;
  243. TArrowLinePart = (alLine, alTopArrow, alBottomArrow);
  244. TArrowLineParts = set of TArrowLinePart;
  245. TGLArrowHeadStyle = (ahssStacked, ahssCentered, ahssIncluded);
  246. (* Draws an arrowhead (cylinder + cone).
  247. The arrow head is a cone that shares the attributes of the cylinder
  248. (ie stacks/slices, materials etc). Seems to work ok.
  249. This is useful for displaying a vector based field (eg velocity) or
  250. other arrows that might be required.
  251. By default the bottom arrow is off *)
  252. TGLArrowLine = class(TGLCylinderBase)
  253. private
  254. FParts: TArrowLineParts;
  255. FTopRadius: Single;
  256. fTopArrowHeadHeight: Single;
  257. fTopArrowHeadRadius: Single;
  258. fBottomArrowHeadHeight: Single;
  259. fBottomArrowHeadRadius: Single;
  260. FHeadStackingStyle: TGLArrowHeadStyle;
  261. protected
  262. procedure SetTopRadius(const aValue: Single);
  263. procedure SetTopArrowHeadHeight(const aValue: Single);
  264. procedure SetTopArrowHeadRadius(const aValue: Single);
  265. procedure SetBottomArrowHeadHeight(const aValue: Single);
  266. procedure SetBottomArrowHeadRadius(const aValue: Single);
  267. procedure SetParts(aValue: TArrowLineParts);
  268. procedure SetHeadStackingStyle(const val: TGLArrowHeadStyle);
  269. public
  270. constructor Create(AOwner: TComponent); override;
  271. procedure BuildList(var rci: TGLRenderContextInfo); override;
  272. procedure Assign(Source: TPersistent); override;
  273. published
  274. property TopRadius: Single read FTopRadius write SetTopRadius;
  275. property HeadStackingStyle: TGLArrowHeadStyle read FHeadStackingStyle
  276. write SetHeadStackingStyle default ahssStacked;
  277. property Parts: TArrowLineParts read FParts write SetParts
  278. default [alLine, alTopArrow];
  279. property TopArrowHeadHeight: Single read fTopArrowHeadHeight
  280. write SetTopArrowHeadHeight;
  281. property TopArrowHeadRadius: Single read fTopArrowHeadRadius
  282. write SetTopArrowHeadRadius;
  283. property BottomArrowHeadHeight: Single read fBottomArrowHeadHeight
  284. write SetBottomArrowHeadHeight;
  285. property BottomArrowHeadRadius: Single read fBottomArrowHeadRadius
  286. write SetBottomArrowHeadRadius;
  287. end;
  288. TArrowArcPart = (aaArc, aaTopArrow, aaBottomArrow);
  289. TGLArrowArcPart = set of TArrowArcPart;
  290. (* Draws an arrowhead (Sliced Torus + cone).
  291. The arrow head is a cone that shares the attributes of the Torus
  292. (ie stacks/slices, materials etc).
  293. This is useful for displaying a movement (eg twist) or
  294. other arc arrows that might be required.
  295. By default the bottom arrow is off *)
  296. TGLArrowArc = class(TGLCylinderBase)
  297. private
  298. fArcRadius: Single;
  299. FStartAngle: Single;
  300. FStopAngle: Single;
  301. FParts: TGLArrowArcPart;
  302. FTopRadius: Single;
  303. fTopArrowHeadHeight: Single;
  304. fTopArrowHeadRadius: Single;
  305. fBottomArrowHeadHeight: Single;
  306. fBottomArrowHeadRadius: Single;
  307. FHeadStackingStyle: TGLArrowHeadStyle;
  308. FMesh: array of array of TVertexRec;
  309. protected
  310. procedure SetArcRadius(const aValue: Single);
  311. procedure SetStartAngle(const aValue: Single);
  312. procedure SetStopAngle(const aValue: Single);
  313. procedure SetTopRadius(const aValue: Single);
  314. procedure SetTopArrowHeadHeight(const aValue: Single);
  315. procedure SetTopArrowHeadRadius(const aValue: Single);
  316. procedure SetBottomArrowHeadHeight(const aValue: Single);
  317. procedure SetBottomArrowHeadRadius(const aValue: Single);
  318. procedure SetParts(aValue: TGLArrowArcPart);
  319. procedure SetHeadStackingStyle(const val: TGLArrowHeadStyle);
  320. public
  321. constructor Create(AOwner: TComponent); override;
  322. procedure BuildList(var rci: TGLRenderContextInfo); override;
  323. procedure Assign(Source: TPersistent); override;
  324. published
  325. property ArcRadius: Single read fArcRadius write SetArcRadius;
  326. property StartAngle: Single read FStartAngle write SetStartAngle;
  327. property StopAngle: Single read FStopAngle write SetStopAngle;
  328. property TopRadius: Single read FTopRadius write SetTopRadius;
  329. property HeadStackingStyle: TGLArrowHeadStyle read FHeadStackingStyle
  330. write SetHeadStackingStyle default ahssStacked;
  331. property Parts: TGLArrowArcPart read FParts write SetParts
  332. default [aaArc, aaTopArrow];
  333. property TopArrowHeadHeight: Single read fTopArrowHeadHeight
  334. write SetTopArrowHeadHeight;
  335. property TopArrowHeadRadius: Single read fTopArrowHeadRadius
  336. write SetTopArrowHeadRadius;
  337. property BottomArrowHeadHeight: Single read fBottomArrowHeadHeight
  338. write SetBottomArrowHeadHeight;
  339. property BottomArrowHeadRadius: Single read fBottomArrowHeadRadius
  340. write SetBottomArrowHeadRadius;
  341. end;
  342. TPolygonPart = (ppTop, ppBottom);
  343. TGLPolygonParts = set of TPolygonPart;
  344. (* A basic polygon object.
  345. The curve is described by the Nodes and SplineMode properties, should be
  346. planar and is automatically tessellated.
  347. Texture coordinates are deduced from X and Y coordinates only.
  348. This object allows only for polygons described by a single curve, if you
  349. need "complex polygons" with holes, patches and cutouts, see GLMultiPolygon. *)
  350. TGLPolygon = class(TGLPolygonBase)
  351. private
  352. FParts: TGLPolygonParts;
  353. protected
  354. procedure SetParts(const val: TGLPolygonParts);
  355. public
  356. constructor Create(AOwner: TComponent); override;
  357. destructor Destroy; override;
  358. procedure Assign(Source: TPersistent); override;
  359. procedure BuildList(var rci: TGLRenderContextInfo); override;
  360. published
  361. (* Parts of polygon.
  362. The 'top' of the polygon is the position were the curve describing
  363. the polygon spin counter-clockwise (i.e. right handed convention). *)
  364. property Parts: TGLPolygonParts read FParts write SetParts default [ppTop, ppBottom];
  365. end;
  366. TFrustrumPart = (fpTop, fpBottom, fpFront, fpBack, fpLeft, fpRight);
  367. TFrustrumParts = set of TFrustrumPart;
  368. const
  369. cAllFrustrumParts = [fpTop, fpBottom, fpFront, fpBack, fpLeft, fpRight];
  370. type
  371. (* A frustrum is a pyramid with the top chopped off.
  372. The height of the imaginary pyramid is ApexHeight, the height of the
  373. frustrum is Height. If ApexHeight and Height are the same, the frustrum
  374. degenerates into a pyramid.
  375. Height cannot be greater than ApexHeight. *)
  376. TGLFrustrum = class(TGLSceneObject)
  377. private
  378. FApexHeight, FBaseDepth, FBaseWidth, FHeight: Single;
  379. FParts: TFrustrumParts;
  380. FNormalDirection: TGLNormalDirection;
  381. procedure SetApexHeight(const aValue: Single);
  382. procedure SetBaseDepth(const aValue: Single);
  383. procedure SetBaseWidth(const aValue: Single);
  384. procedure SetHeight(const aValue: Single);
  385. procedure SetParts(aValue: TFrustrumParts);
  386. procedure SetNormalDirection(aValue: TGLNormalDirection);
  387. protected
  388. procedure DefineProperties(Filer: TFiler); override;
  389. procedure ReadData(Stream: TStream);
  390. procedure WriteData(Stream: TStream);
  391. public
  392. constructor Create(AOwner: TComponent); override;
  393. procedure BuildList(var rci: TGLRenderContextInfo); override;
  394. procedure Assign(Source: TPersistent); override;
  395. function TopDepth: Single;
  396. function TopWidth: Single;
  397. function AxisAlignedBoundingBoxUnscaled: TAABB;
  398. function AxisAlignedDimensionsUnscaled: TVector; override;
  399. published
  400. property ApexHeight: Single read FApexHeight write SetApexHeight stored False;
  401. property BaseDepth: Single read FBaseDepth write SetBaseDepth stored False;
  402. property BaseWidth: Single read FBaseWidth write SetBaseWidth stored False;
  403. property Height: Single read FHeight write SetHeight stored False;
  404. property NormalDirection: TGLNormalDirection read FNormalDirection
  405. write SetNormalDirection default ndOutside;
  406. property Parts: TFrustrumParts read FParts write SetParts default cAllFrustrumParts;
  407. end;
  408. // -------------------------------------------------------------
  409. implementation
  410. // -------------------------------------------------------------
  411. // ------------------
  412. // ------------------ TGLDisk ------------------
  413. // ------------------
  414. constructor TGLDisk.Create(AOwner: TComponent);
  415. begin
  416. inherited Create(AOwner);
  417. FOuterRadius := 0.5;
  418. FInnerRadius := 0;
  419. FSlices := 16;
  420. FLoops := 2;
  421. FStartAngle := 0;
  422. FSweepAngle := 360;
  423. end;
  424. procedure TGLDisk.BuildList(var rci: TGLRenderContextInfo);
  425. var
  426. quadric: PGLUquadric;
  427. // quadric: GLUquadricObj; from Winapi.OpenGL
  428. begin
  429. quadric := gluNewQuadric();
  430. SetupQuadricParams(quadric);
  431. gluPartialDisk(quadric, FInnerRadius, FOuterRadius, FSlices, FLoops,
  432. FStartAngle, FSweepAngle);
  433. gluDeleteQuadric(quadric);
  434. end;
  435. procedure TGLDisk.SetOuterRadius(const aValue: Single);
  436. begin
  437. if aValue <> FOuterRadius then
  438. begin
  439. FOuterRadius := aValue;
  440. StructureChanged;
  441. end;
  442. end;
  443. procedure TGLDisk.SetInnerRadius(const aValue: Single);
  444. begin
  445. if aValue <> FInnerRadius then
  446. begin
  447. FInnerRadius := aValue;
  448. StructureChanged;
  449. end;
  450. end;
  451. procedure TGLDisk.SetSlices(aValue: integer);
  452. begin
  453. if aValue <> FSlices then
  454. begin
  455. FSlices := aValue;
  456. StructureChanged;
  457. end;
  458. end;
  459. procedure TGLDisk.SetLoops(aValue: integer);
  460. begin
  461. if aValue <> FLoops then
  462. begin
  463. FLoops := aValue;
  464. StructureChanged;
  465. end;
  466. end;
  467. procedure TGLDisk.SetStartAngle(const aValue: Single);
  468. begin
  469. if aValue <> FStartAngle then
  470. begin
  471. FStartAngle := aValue;
  472. StructureChanged;
  473. end;
  474. end;
  475. procedure TGLDisk.SetSweepAngle(const aValue: Single);
  476. begin
  477. if aValue <> FSweepAngle then
  478. begin
  479. FSweepAngle := aValue;
  480. StructureChanged;
  481. end;
  482. end;
  483. procedure TGLDisk.Assign(Source: TPersistent);
  484. begin
  485. if Assigned(Source) and (Source is TGLDisk) then
  486. begin
  487. FOuterRadius := TGLDisk(Source).FOuterRadius;
  488. FInnerRadius := TGLDisk(Source).FInnerRadius;
  489. FSlices := TGLDisk(Source).FSlices;
  490. FLoops := TGLDisk(Source).FLoops;
  491. FStartAngle := TGLDisk(Source).FStartAngle;
  492. FSweepAngle := TGLDisk(Source).FSweepAngle;
  493. end;
  494. inherited Assign(Source);
  495. end;
  496. function TGLDisk.AxisAlignedDimensionsUnscaled: TVector;
  497. var
  498. r: Single;
  499. begin
  500. r := Abs(FOuterRadius);
  501. Result := VectorMake(r, r, 0);
  502. end;
  503. function TGLDisk.RayCastIntersect(const rayStart, rayVector: TVector;
  504. intersectPoint: PVector = nil; intersectNormal: PVector = nil): Boolean;
  505. var
  506. ip: TVector;
  507. d: Single;
  508. angle, beginAngle, endAngle: Single;
  509. localIntPoint: TVector;
  510. begin
  511. Result := False;
  512. if SweepAngle > 0 then
  513. if RayCastPlaneIntersect(rayStart, rayVector, AbsolutePosition,
  514. AbsoluteDirection, @ip) then
  515. begin
  516. if Assigned(intersectPoint) then
  517. SetVector(intersectPoint^, ip);
  518. localIntPoint := AbsoluteToLocal(ip);
  519. d := VectorNorm(localIntPoint);
  520. if (d >= Sqr(InnerRadius)) and (d <= Sqr(OuterRadius)) then
  521. begin
  522. if SweepAngle >= 360 then
  523. Result := true
  524. else
  525. begin
  526. // arctan2 returns results between -pi and +pi, we want between 0 and 360
  527. angle := 180 / pi * ArcTan2(localIntPoint.X, localIntPoint.Y);
  528. if angle < 0 then
  529. angle := angle + 360;
  530. // we also want StartAngle and StartAngle+SweepAngle to be in this range
  531. beginAngle := Trunc(StartAngle) mod 360;
  532. endAngle := Trunc(StartAngle + SweepAngle) mod 360;
  533. // If beginAngle>endAngle then area crosses the boundary from 360=>0 degrees
  534. // therefore have 2 valid regions (beginAngle to 360) & (0 to endAngle)
  535. // otherwise just 1 valid region (beginAngle to endAngle)
  536. if beginAngle > endAngle then
  537. begin
  538. if (angle > beginAngle) or (angle < endAngle) then
  539. Result := true;
  540. end
  541. else if (angle > beginAngle) and (angle < endAngle) then
  542. Result := true;
  543. end;
  544. end;
  545. end;
  546. if Result = true then
  547. if Assigned(intersectNormal) then
  548. SetVector(intersectNormal^, AbsoluteUp);
  549. end;
  550. // ------------------
  551. // ------------------ TGLCylinderBase ------------------
  552. // ------------------
  553. constructor TGLCylinderBase.Create(AOwner: TComponent);
  554. begin
  555. inherited Create(AOwner);
  556. FBottomRadius := 0.5;
  557. FHeight := 1;
  558. FSlices := 16;
  559. FStacks := 4;
  560. FLoops := 1;
  561. end;
  562. procedure TGLCylinderBase.SetBottomRadius(const aValue: Single);
  563. begin
  564. if aValue <> FBottomRadius then
  565. begin
  566. FBottomRadius := aValue;
  567. StructureChanged;
  568. end;
  569. end;
  570. function TGLCylinderBase.GetTopRadius: Single;
  571. begin
  572. Result := FBottomRadius;
  573. end;
  574. procedure TGLCylinderBase.SetHeight(const aValue: Single);
  575. begin
  576. if aValue <> FHeight then
  577. begin
  578. FHeight := aValue;
  579. StructureChanged;
  580. end;
  581. end;
  582. procedure TGLCylinderBase.SetSlices(aValue: Integer);
  583. begin
  584. if aValue <> FSlices then
  585. begin
  586. FSlices := aValue;
  587. StructureChanged;
  588. end;
  589. end;
  590. procedure TGLCylinderBase.SetStacks(aValue: Integer);
  591. begin
  592. if aValue <> FStacks then
  593. begin
  594. FStacks := aValue;
  595. StructureChanged;
  596. end;
  597. end;
  598. procedure TGLCylinderBase.SetLoops(aValue: Integer);
  599. begin
  600. if (aValue >= 1) and (aValue <> FLoops) then
  601. begin
  602. FLoops := aValue;
  603. StructureChanged;
  604. end;
  605. end;
  606. procedure TGLCylinderBase.Assign(Source: TPersistent);
  607. begin
  608. if Assigned(Source) and (Source is TGLCylinderBase) then
  609. begin
  610. FBottomRadius := TGLCylinderBase(Source).FBottomRadius;
  611. FSlices := TGLCylinderBase(Source).FSlices;
  612. FStacks := TGLCylinderBase(Source).FStacks;
  613. FLoops := TGLCylinderBase(Source).FLoops;
  614. FHeight := TGLCylinderBase(Source).FHeight;
  615. end;
  616. inherited Assign(Source);
  617. end;
  618. function TGLCylinderBase.GenerateSilhouette(const silhouetteParameters
  619. : TGLSilhouetteParameters): TGLSilhouette;
  620. var
  621. connectivity: TConnectivity;
  622. sil: TGLSilhouette;
  623. ShadowSlices: integer;
  624. i: integer;
  625. p: array [0 .. 3] of TVector3f;
  626. PiDivSlices: Single;
  627. a1, a2: Single;
  628. c1, c2: TVector3f;
  629. cosa1, cosa2, sina1, sina2: Single;
  630. HalfHeight: Single;
  631. ShadowTopRadius: Single;
  632. begin
  633. connectivity := TConnectivity.Create(true);
  634. ShadowSlices := FSlices div 1;
  635. if FSlices < 5 then
  636. FSlices := 5;
  637. PiDivSlices := 2 * pi / ShadowSlices;
  638. a1 := 0;
  639. // Is this a speed improvement or just a waste of code?
  640. HalfHeight := FHeight / 2;
  641. MakeVector(c1, 0, -HalfHeight, 0);
  642. MakeVector(c2, 0, HalfHeight, 0);
  643. ShadowTopRadius := GetTopRadius;
  644. for i := 0 to ShadowSlices - 1 do
  645. begin
  646. a2 := a1 + PiDivSlices;
  647. // Is this a speed improvement or just a waste of code?
  648. cosa1 := cos(a1);
  649. cosa2 := cos(a2);
  650. sina1 := sin(a1);
  651. sina2 := sin(a2);
  652. // Generate the four "corners";
  653. // Bottom corners
  654. MakeVector(p[0], FBottomRadius * sina2, -HalfHeight, FBottomRadius * cosa2);
  655. MakeVector(p[1], FBottomRadius * sina1, -HalfHeight, FBottomRadius * cosa1);
  656. // Top corners
  657. MakeVector(p[2], ShadowTopRadius * sina1, HalfHeight,
  658. ShadowTopRadius * cosa1);
  659. MakeVector(p[3], ShadowTopRadius * sina2, HalfHeight,
  660. ShadowTopRadius * cosa2); // }
  661. // This should be optimized to use AddIndexedFace, because this method
  662. // searches for each of the vertices and adds them or re-uses them.
  663. // Skin
  664. connectivity.AddFace(p[2], p[1], p[0]);
  665. connectivity.AddFace(p[3], p[2], p[0]);
  666. // Sides / caps
  667. connectivity.AddFace(c1, p[0], p[1]);
  668. connectivity.AddFace(p[2], p[3], c2);
  669. a1 := a1 + PiDivSlices;
  670. end;
  671. sil := nil;
  672. connectivity.CreateSilhouette(silhouetteParameters, sil, False);
  673. Result := sil;
  674. connectivity.Free;
  675. end;
  676. // ------------------
  677. // ------------------ TGLCone ------------------
  678. // ------------------
  679. constructor TGLCone.Create(AOwner: TComponent);
  680. begin
  681. inherited Create(AOwner);
  682. FParts := [coSides, coBottom];
  683. end;
  684. procedure TGLCone.BuildList(var rci: TGLRenderContextInfo);
  685. var
  686. quadric: PGLUquadricObj;
  687. begin
  688. gl.PushMatrix;
  689. quadric := gluNewQuadric();
  690. SetupQuadricParams(quadric);
  691. gl.Rotated(-90, 1, 0, 0);
  692. gl.Translatef(0, 0, -FHeight * 0.5);
  693. if coSides in FParts then
  694. gluCylinder(quadric, BottomRadius, 0, Height, Slices, Stacks);
  695. if coBottom in FParts then
  696. begin
  697. // top of a disk is defined as outside
  698. SetInvertedQuadricOrientation(quadric);
  699. gluDisk(quadric, 0, BottomRadius, Slices, FLoops);
  700. end;
  701. gluDeleteQuadric(quadric);
  702. gl.PopMatrix;
  703. end;
  704. procedure TGLCone.SetParts(aValue: TConeParts);
  705. begin
  706. if aValue <> FParts then
  707. begin
  708. FParts := aValue;
  709. StructureChanged;
  710. end;
  711. end;
  712. procedure TGLCone.Assign(Source: TPersistent);
  713. begin
  714. if Assigned(Source) and (Source is TGLCone) then
  715. begin
  716. FParts := TGLCone(Source).FParts;
  717. end;
  718. inherited Assign(Source);
  719. end;
  720. function TGLCone.AxisAlignedDimensionsUnscaled: TVector;
  721. var
  722. r: Single;
  723. begin
  724. r := Abs(FBottomRadius);
  725. Result := VectorMake(r { *Scale.DirectX } , 0.5 * FHeight { *Scale.DirectY } ,
  726. r { *Scale.DirectZ } );
  727. end;
  728. function TGLCone.GetTopRadius: Single;
  729. begin
  730. Result := 0;
  731. end;
  732. function TGLCone.RayCastIntersect(const rayStart, rayVector: TVector;
  733. intersectPoint: PVector = nil; intersectNormal: PVector = nil): Boolean;
  734. var
  735. ip, localRayStart, localRayVector: TVector;
  736. poly: array [0 .. 2] of Double;
  737. roots: TDoubleArray;
  738. minRoot: Double;
  739. d, t, hconst: Single;
  740. begin
  741. Result := False;
  742. localRayStart := AbsoluteToLocal(rayStart);
  743. localRayVector := VectorNormalize(AbsoluteToLocal(rayVector));
  744. if coBottom in Parts then
  745. begin
  746. // bottom can only be raycast from beneath
  747. if localRayStart.Y < -FHeight * 0.5 then
  748. begin
  749. if RayCastPlaneIntersect(localRayStart, localRayVector,
  750. PointMake(0, -FHeight * 0.5, 0), YHmgVector, @ip) then
  751. begin
  752. d := VectorNorm(ip.X, ip.Z);
  753. if (d <= Sqr(BottomRadius)) then
  754. begin
  755. Result := true;
  756. if Assigned(intersectPoint) then
  757. SetVector(intersectPoint^, LocalToAbsolute(ip));
  758. if Assigned(intersectNormal) then
  759. SetVector(intersectNormal^, VectorNegate(AbsoluteUp));
  760. Exit;
  761. end;
  762. end;
  763. end;
  764. end;
  765. if coSides in Parts then
  766. begin
  767. hconst := -Sqr(BottomRadius) / Sqr(Height);
  768. // intersect against infinite cones (in positive and negative direction)
  769. poly[0] := Sqr(localRayStart.X) + hconst *
  770. Sqr(localRayStart.Y - 0.5 * FHeight) +
  771. Sqr(localRayStart.Z);
  772. poly[1] := 2 * (localRayStart.X * localRayVector.X + hconst *
  773. (localRayStart.Y - 0.5 * FHeight) * localRayVector.Y +
  774. localRayStart.Z* localRayVector.Z);
  775. poly[2] := Sqr(localRayVector.X) + hconst * Sqr(localRayVector.Y) +
  776. Sqr(localRayVector.Z);
  777. SetLength(roots, 0);
  778. roots := SolveQuadric(@poly);
  779. if MinPositiveCoef(roots, minRoot) then
  780. begin
  781. t := minRoot;
  782. ip := VectorCombine(localRayStart, localRayVector, 1, t);
  783. // check that intersection with infinite cone is within the range we want
  784. if (ip.Y > -FHeight * 0.5) and (ip.Y < FHeight * 0.5) then
  785. begin
  786. Result := true;
  787. if Assigned(intersectPoint) then
  788. intersectPoint^ := LocalToAbsolute(ip);
  789. if Assigned(intersectNormal) then
  790. begin
  791. ip.Y := hconst * (ip.Y - 0.5 * Height);
  792. ip.W := 0;
  793. NormalizeVector(ip);
  794. intersectNormal^ := LocalToAbsolute(ip);
  795. end;
  796. end;
  797. end;
  798. end;
  799. end;
  800. // ------------------
  801. // ------------------ TGLCylinder ------------------
  802. // ------------------
  803. constructor TGLCylinder.Create(AOwner: TComponent);
  804. begin
  805. inherited Create(AOwner);
  806. FTopRadius := 0.5;
  807. FParts := [cySides, cyBottom, cyTop];
  808. FAlignment := caCenter;
  809. end;
  810. procedure TGLCylinder.BuildList(var rci: TGLRenderContextInfo);
  811. var
  812. quadric: PGLUquadricObj;
  813. begin
  814. gl.PushMatrix;
  815. quadric := gluNewQuadric;
  816. SetupQuadricParams(quadric);
  817. gl.Rotatef(-90, 1, 0, 0);
  818. case Alignment of
  819. caTop:
  820. gl.Translatef(0, 0, -FHeight);
  821. caBottom:
  822. ;
  823. else // caCenter
  824. gl.Translatef(0, 0, -FHeight * 0.5);
  825. end;
  826. if cySides in FParts then
  827. gluCylinder(quadric, FBottomRadius, FTopRadius, FHeight, FSlices, FStacks);
  828. if cyTop in FParts then
  829. begin
  830. gl.PushMatrix;
  831. gl.Translatef(0, 0, FHeight);
  832. gluDisk(Quadric, 0, FTopRadius, FSlices, FLoops);
  833. gl.PopMatrix;
  834. end;
  835. if cyBottom in FParts then
  836. begin
  837. // swap quadric orientation because top of a disk is defined as outside
  838. SetInvertedQuadricOrientation(quadric);
  839. gluDisk(quadric, 0, FBottomRadius, FSlices, FLoops);
  840. end;
  841. gluDeleteQuadric(Quadric);
  842. gl.PopMatrix;
  843. end;
  844. procedure TGLCylinder.SetTopRadius(const aValue: Single);
  845. begin
  846. if aValue <> FTopRadius then
  847. begin
  848. FTopRadius := aValue;
  849. StructureChanged;
  850. end;
  851. end;
  852. function TGLCylinder.GetTopRadius: Single;
  853. begin
  854. Result := FTopRadius;
  855. end;
  856. procedure TGLCylinder.SetParts(aValue: TCylinderParts);
  857. begin
  858. if aValue <> FParts then
  859. begin
  860. FParts := aValue;
  861. StructureChanged;
  862. end;
  863. end;
  864. procedure TGLCylinder.SetAlignment(val: TCylinderAlignment);
  865. begin
  866. if val <> FAlignment then
  867. begin
  868. FAlignment := val;
  869. StructureChanged;
  870. end;
  871. end;
  872. procedure TGLCylinder.Assign(Source: TPersistent);
  873. begin
  874. if Assigned(Source) and (Source is TGLCylinder) then
  875. begin
  876. FParts := TGLCylinder(Source).FParts;
  877. FTopRadius := TGLCylinder(Source).FTopRadius;
  878. end;
  879. inherited Assign(Source);
  880. end;
  881. function TGLCylinder.AxisAlignedDimensionsUnscaled: TVector;
  882. var
  883. r, r1: Single;
  884. begin
  885. r := Abs(FBottomRadius);
  886. r1 := Abs(FTopRadius);
  887. if r1 > r then
  888. r := r1;
  889. Result := VectorMake(r, 0.5 * FHeight, r);
  890. // ScaleVector(Result, Scale.AsVector);
  891. end;
  892. function TGLCylinder.RayCastIntersect(const rayStart, rayVector: TVector;
  893. intersectPoint: PVector = nil; intersectNormal: PVector = nil): Boolean;
  894. const
  895. cOne: Single = 1;
  896. var
  897. locRayStart, locRayVector, ip: TVector;
  898. poly: array [0 .. 2] of Double;
  899. roots: TDoubleArray;
  900. minRoot: Double;
  901. t, tr2, invRayVector1, hTop, hBottom: Single;
  902. tPlaneMin, tPlaneMax: Single;
  903. begin
  904. Result := False;
  905. locRayStart := AbsoluteToLocal(rayStart);
  906. locRayVector := AbsoluteToLocal(rayVector);
  907. case Alignment of
  908. caTop:
  909. begin
  910. hTop := 0;
  911. hBottom := -Height;
  912. end;
  913. caBottom:
  914. begin
  915. hTop := Height;
  916. hBottom := 0;
  917. end;
  918. else
  919. // caCenter
  920. hTop := Height * 0.5;
  921. hBottom := -hTop;
  922. end;
  923. if locRayVector.Y = 0 then
  924. begin
  925. // intersect if ray shot through the top/bottom planes
  926. if (locRayStart.X > hTop) or (locRayStart.X < hBottom) then
  927. Exit;
  928. tPlaneMin := -1E99;
  929. tPlaneMax := 1E99;
  930. end
  931. else
  932. begin
  933. invRayVector1 := cOne / locRayVector.Y;
  934. tr2 := Sqr(TopRadius);
  935. // compute intersection with topPlane
  936. t := (hTop - locRayStart.Y) * invRayVector1;
  937. if (t > 0) and (cyTop in Parts) then
  938. begin
  939. ip.X := locRayStart.X + t * locRayVector.X;
  940. ip.Z := locRayStart.Z + t * locRayVector.Z;
  941. if Sqr(ip.X) + Sqr(ip.Z) <= tr2 then
  942. begin
  943. // intersect with top plane
  944. if Assigned(intersectPoint) then
  945. intersectPoint^ := LocalToAbsolute(VectorMake(ip.X, hTop, ip.Z, 1));
  946. if Assigned(intersectNormal) then
  947. intersectNormal^ := LocalToAbsolute(YHmgVector);
  948. Result := true;
  949. end;
  950. end;
  951. tPlaneMin := t;
  952. tPlaneMax := t;
  953. // compute intersection with bottomPlane
  954. t := (hBottom - locRayStart.Y) * invRayVector1;
  955. if (t > 0) and (cyBottom in Parts) then
  956. begin
  957. ip.X := locRayStart.X + t * locRayVector.X;
  958. ip.Z := locRayStart.Z + t * locRayVector.Z;
  959. if (t < tPlaneMin) or (not(cyTop in Parts)) then
  960. begin
  961. if Sqr(ip.X) + Sqr(ip.Z) <= tr2 then
  962. begin
  963. // intersect with top plane
  964. if Assigned(intersectPoint) then
  965. intersectPoint^ := LocalToAbsolute(VectorMake(ip.X, hBottom,
  966. ip.Z, 1));
  967. if Assigned(intersectNormal) then
  968. intersectNormal^ := LocalToAbsolute(VectorNegate(YHmgVector));
  969. Result := true;
  970. end;
  971. end;
  972. end;
  973. if t < tPlaneMin then
  974. tPlaneMin := t;
  975. if t > tPlaneMax then
  976. tPlaneMax := t;
  977. end;
  978. if cySides in Parts then
  979. begin
  980. // intersect against cylinder infinite cylinder
  981. poly[0] := Sqr(locRayStart.X) + Sqr(locRayStart.Z) - Sqr(TopRadius);
  982. poly[1] := 2 * (locRayStart.X * locRayVector.X + locRayStart.Z *
  983. locRayVector.Z);
  984. poly[2] := Sqr(locRayVector.X) + Sqr(locRayVector.Z);
  985. roots := SolveQuadric(@poly);
  986. if MinPositiveCoef(roots, minRoot) then
  987. begin
  988. t := minRoot;
  989. if (t >= tPlaneMin) and (t < tPlaneMax) then
  990. begin
  991. if Assigned(intersectPoint) or Assigned(intersectNormal) then
  992. begin
  993. ip := VectorCombine(locRayStart, locRayVector, 1, t);
  994. if Assigned(intersectPoint) then
  995. intersectPoint^ := LocalToAbsolute(ip);
  996. if Assigned(intersectNormal) then
  997. begin
  998. ip.Y := 0;
  999. ip.W := 0;
  1000. intersectNormal^ := LocalToAbsolute(ip);
  1001. end;
  1002. end;
  1003. Result := true;
  1004. end;
  1005. end;
  1006. end
  1007. else
  1008. SetLength(roots, 0);
  1009. end;
  1010. procedure TGLCylinder.Align(const startPoint, endPoint: TVector);
  1011. var
  1012. dir: TAffineVector;
  1013. begin
  1014. AbsolutePosition := startPoint;
  1015. VectorSubtract(endPoint, startPoint, dir);
  1016. if Parent <> nil then
  1017. dir := Parent.AbsoluteToLocal(dir);
  1018. Up.AsAffineVector := dir;
  1019. Height := VectorLength(dir);
  1020. Lift(Height * 0.5);
  1021. Alignment := caCenter;
  1022. end;
  1023. procedure TGLCylinder.Align(const startObj, endObj: TGLBaseSceneObject);
  1024. begin
  1025. Align(startObj.AbsolutePosition, endObj.AbsolutePosition);
  1026. end;
  1027. procedure TGLCylinder.Align(const startPoint, endPoint: TAffineVector);
  1028. begin
  1029. Align(PointMake(startPoint), PointMake(endPoint));
  1030. end;
  1031. // ------------------
  1032. // ------------------ TGLCapsule ------------------
  1033. // ------------------
  1034. constructor TGLCapsule.Create(AOwner: TComponent);
  1035. begin
  1036. inherited Create(AOwner);
  1037. FHeight := 1;
  1038. FRadius := 0.5;
  1039. FSlices := 4;
  1040. FStacks := 4;
  1041. FParts := [cySides, cyBottom, cyTop];
  1042. FAlignment := caCenter;
  1043. end;
  1044. procedure TGLCapsule.BuildList(var rci: TGLRenderContextInfo);
  1045. var
  1046. i, j, n: integer;
  1047. start_nx2: Single;
  1048. start_ny2: Single;
  1049. tmp, nx, ny, nz, start_nx, start_ny, a, ca, sa, l: Single;
  1050. nx2, ny2, nz2: Single;
  1051. begin
  1052. gl.PushMatrix;
  1053. gl.Rotatef(-90, 0, 0, 1);
  1054. case Alignment of
  1055. caTop:
  1056. gl.Translatef(0, 0, FHeight + 1);
  1057. caBottom:
  1058. gl.Translatef(0, 0, -FHeight);
  1059. else // caCenter
  1060. gl.Translatef(0, 0, 0.5);
  1061. end;
  1062. n := FSlices * FStacks;
  1063. l := FHeight;
  1064. l := l * 0.5;
  1065. a := (pi * 2.0) / n;
  1066. sa := sin(a);
  1067. ca := cos(a);
  1068. ny := 0;
  1069. nz := 1;
  1070. if cySides in FParts then
  1071. begin
  1072. gl.Begin_(GL_TRIANGLE_STRIP);
  1073. for i := 0 to n do
  1074. begin
  1075. gl.Normal3d(ny, nz, 0);
  1076. gl.TexCoord2f(i / n, 1);
  1077. gl.Vertex3d(ny * FRadius, nz * FRadius, l - 0.5);
  1078. gl.Normal3d(ny, nz, 0);
  1079. gl.TexCoord2f(i / n, 0);
  1080. gl.Vertex3d(ny * FRadius, nz * FRadius, -l - 0.5);
  1081. tmp := ca * ny - sa * nz;
  1082. nz := sa * ny + ca * nz;
  1083. ny := tmp;
  1084. end;
  1085. gl.End_();
  1086. end;
  1087. //
  1088. if cyTop in FParts then
  1089. begin
  1090. start_nx := 0;
  1091. start_ny := 1;
  1092. for j := 0 to (n div FStacks) do
  1093. begin
  1094. start_nx2 := ca * start_nx + sa * start_ny;
  1095. start_ny2 := -sa * start_nx + ca * start_ny;
  1096. nx := start_nx;
  1097. ny := start_ny;
  1098. nz := 0;
  1099. nx2 := start_nx2;
  1100. ny2 := start_ny2;
  1101. nz2 := 0;
  1102. gl.PushMatrix;
  1103. gl.Translatef(0, 0, -0.5);
  1104. gl.Begin_(GL_TRIANGLE_STRIP);
  1105. for i := 0 to n do
  1106. begin
  1107. gl.Normal3d(ny2, nz2, nx2);
  1108. gl.TexCoord2f(i / n, j / n);
  1109. gl.Vertex3d(ny2 * FRadius, nz2 * FRadius, l + nx2 * FRadius);
  1110. gl.Normal3d(ny, nz, nx);
  1111. gl.TexCoord2f(i / n, (j - 1) / n);
  1112. gl.Vertex3d(ny * FRadius, nz * FRadius, l + nx * FRadius);
  1113. tmp := ca * ny - sa * nz;
  1114. nz := sa * ny + ca * nz;
  1115. ny := tmp;
  1116. tmp := ca * ny2 - sa * nz2;
  1117. nz2 := sa * ny2 + ca * nz2;
  1118. ny2 := tmp;
  1119. end;
  1120. gl.End_();
  1121. gl.PopMatrix;
  1122. start_nx := start_nx2;
  1123. start_ny := start_ny2;
  1124. end;
  1125. end;
  1126. //
  1127. if cyBottom in FParts then
  1128. begin
  1129. start_nx := 0;
  1130. start_ny := 1;
  1131. for j := 0 to (n div FStacks) do
  1132. begin
  1133. start_nx2 := ca * start_nx - sa * start_ny;
  1134. start_ny2 := sa * start_nx + ca * start_ny;
  1135. nx := start_nx;
  1136. ny := start_ny;
  1137. nz := 0;
  1138. nx2 := start_nx2;
  1139. ny2 := start_ny2;
  1140. nz2 := 0;
  1141. gl.PushMatrix;
  1142. gl.Translatef(0, 0, -0.5);
  1143. gl.Begin_(GL_TRIANGLE_STRIP);
  1144. for i := 0 to n do
  1145. begin
  1146. gl.Normal3d(ny, nz, nx);
  1147. gl.TexCoord2f(i / n, (j - 1) / n);
  1148. gl.Vertex3d(ny * FRadius, nz * FRadius, -l + nx * FRadius);
  1149. gl.Normal3d(ny2, nz2, nx2);
  1150. gl.TexCoord2f(i / n, j / n);
  1151. gl.Vertex3d(ny2 * FRadius, nz2 * FRadius, -l + nx2 * FRadius);
  1152. tmp := ca * ny - sa * nz;
  1153. nz := sa * ny + ca * nz;
  1154. ny := tmp;
  1155. tmp := ca * ny2 - sa * nz2;
  1156. nz2 := sa * ny2 + ca * nz2;
  1157. ny2 := tmp;
  1158. end;
  1159. gl.End_();
  1160. gl.PopMatrix;
  1161. start_nx := start_nx2;
  1162. start_ny := start_ny2;
  1163. end;
  1164. end;
  1165. gl.PopMatrix;
  1166. end;
  1167. procedure TGLCapsule.SetHeight(const aValue: Single);
  1168. begin
  1169. if aValue <> FHeight then
  1170. begin
  1171. FHeight := aValue;
  1172. StructureChanged;
  1173. end;
  1174. end;
  1175. procedure TGLCapsule.SetRadius(const aValue: Single);
  1176. begin
  1177. if aValue <> FRadius then
  1178. begin
  1179. FRadius := aValue;
  1180. StructureChanged;
  1181. end;
  1182. end;
  1183. procedure TGLCapsule.SetSlices(const aValue: integer);
  1184. begin
  1185. if aValue <> FSlices then
  1186. begin
  1187. FSlices := aValue;
  1188. StructureChanged;
  1189. end;
  1190. end;
  1191. procedure TGLCapsule.SetStacks(const aValue: integer);
  1192. begin
  1193. if aValue <> FStacks then
  1194. begin
  1195. FStacks := aValue;
  1196. StructureChanged;
  1197. end;
  1198. end;
  1199. procedure TGLCapsule.SetParts(aValue: TCylinderParts);
  1200. begin
  1201. if aValue <> FParts then
  1202. begin
  1203. FParts := aValue;
  1204. StructureChanged;
  1205. end;
  1206. end;
  1207. procedure TGLCapsule.SetAlignment(val: TCylinderAlignment);
  1208. begin
  1209. if val <> FAlignment then
  1210. begin
  1211. FAlignment := val;
  1212. StructureChanged;
  1213. end;
  1214. end;
  1215. procedure TGLCapsule.Assign(Source: TPersistent);
  1216. begin
  1217. if Assigned(Source) and (Source is TGLCapsule) then
  1218. begin
  1219. FParts := TGLCapsule(Source).FParts;
  1220. FRadius := TGLCapsule(Source).FRadius;
  1221. end;
  1222. inherited Assign(Source);
  1223. end;
  1224. function TGLCapsule.AxisAlignedDimensionsUnscaled: TVector;
  1225. var
  1226. r, r1: Single;
  1227. begin
  1228. r := Abs(FRadius);
  1229. r1 := Abs(FRadius);
  1230. if r1 > r then
  1231. r := r1;
  1232. Result := VectorMake(r, 0.5 * FHeight, r);
  1233. // ScaleVector(Result, Scale.AsVector);
  1234. end;
  1235. function TGLCapsule.RayCastIntersect(const rayStart, rayVector: TVector;
  1236. intersectPoint: PVector = nil; intersectNormal: PVector = nil): Boolean;
  1237. const
  1238. cOne: Single = 1;
  1239. var
  1240. locRayStart, locRayVector, ip: TVector;
  1241. poly: array [0 .. 2] of Double;
  1242. roots: TDoubleArray;
  1243. minRoot: Double;
  1244. t, tr2, invRayVector1, hTop, hBottom: Single;
  1245. tPlaneMin, tPlaneMax: Single;
  1246. begin
  1247. Result := False;
  1248. locRayStart := AbsoluteToLocal(rayStart);
  1249. locRayVector := AbsoluteToLocal(rayVector);
  1250. case Alignment of
  1251. caTop:
  1252. begin
  1253. hTop := 0;
  1254. hBottom := -FHeight;
  1255. end;
  1256. caBottom:
  1257. begin
  1258. hTop := FHeight;
  1259. hBottom := 0;
  1260. end;
  1261. else
  1262. // caCenter
  1263. hTop := FHeight * 0.5;
  1264. hBottom := -hTop;
  1265. end;
  1266. if locRayVector.Y = 0 then
  1267. begin
  1268. // intersect if ray shot through the top/bottom planes
  1269. if (locRayStart.X > hTop) or (locRayStart.X < hBottom) then
  1270. Exit;
  1271. tPlaneMin := -1E99;
  1272. tPlaneMax := 1E99;
  1273. end
  1274. else
  1275. begin
  1276. invRayVector1 := cOne / locRayVector.Y;
  1277. tr2 := Sqr(Radius);
  1278. // compute intersection with topPlane
  1279. t := (hTop - locRayStart.Y) * invRayVector1;
  1280. if (t > 0) and (cyTop in Parts) then
  1281. begin
  1282. ip.X := locRayStart.X + t * locRayVector.X;
  1283. ip.Z := locRayStart.Z + t * locRayVector.Z;
  1284. if Sqr(ip.X) + Sqr(ip.Z) <= tr2 then
  1285. begin
  1286. // intersect with top plane
  1287. if Assigned(intersectPoint) then
  1288. intersectPoint^ := LocalToAbsolute(VectorMake(ip.X, hTop, ip.Z, 1));
  1289. if Assigned(intersectNormal) then
  1290. intersectNormal^ := LocalToAbsolute(YHmgVector);
  1291. Result := true;
  1292. end;
  1293. end;
  1294. tPlaneMin := t;
  1295. tPlaneMax := t;
  1296. // compute intersection with bottomPlane
  1297. t := (hBottom - locRayStart.Y) * invRayVector1;
  1298. if (t > 0) and (cyBottom in Parts) then
  1299. begin
  1300. ip.X := locRayStart.X + t * locRayVector.X;
  1301. ip.Z := locRayStart.Z + t * locRayVector.Z;
  1302. if (t < tPlaneMin) or (not(cyTop in Parts)) then
  1303. begin
  1304. if Sqr(ip.X) + Sqr(ip.Z) <= tr2 then
  1305. begin
  1306. // intersect with top plane
  1307. if Assigned(intersectPoint) then
  1308. intersectPoint^ := LocalToAbsolute(VectorMake(ip.X, hBottom,
  1309. ip.Z, 1));
  1310. if Assigned(intersectNormal) then
  1311. intersectNormal^ := LocalToAbsolute(VectorNegate(YHmgVector));
  1312. Result := true;
  1313. end;
  1314. end;
  1315. end;
  1316. if t < tPlaneMin then
  1317. tPlaneMin := t;
  1318. if t > tPlaneMax then
  1319. tPlaneMax := t;
  1320. end;
  1321. if cySides in Parts then
  1322. begin
  1323. // intersect against cylinder infinite cylinder
  1324. poly[0] := Sqr(locRayStart.X) + Sqr(locRayStart.Z) - Sqr(Radius);
  1325. poly[1] := 2 * (locRayStart.X * locRayVector.X +
  1326. locRayStart.Z * locRayVector.Z);
  1327. poly[2] := Sqr(locRayVector.X) + Sqr(locRayVector.Z);
  1328. roots := SolveQuadric(@poly);
  1329. if MinPositiveCoef(roots, minRoot) then
  1330. begin
  1331. t := minRoot;
  1332. if (t >= tPlaneMin) and (t < tPlaneMax) then
  1333. begin
  1334. if Assigned(intersectPoint) or Assigned(intersectNormal) then
  1335. begin
  1336. ip := VectorCombine(locRayStart, locRayVector, 1, t);
  1337. if Assigned(intersectPoint) then
  1338. intersectPoint^ := LocalToAbsolute(ip);
  1339. if Assigned(intersectNormal) then
  1340. begin
  1341. ip.Y := 0;
  1342. ip.W := 0;
  1343. intersectNormal^ := LocalToAbsolute(ip);
  1344. end;
  1345. end;
  1346. Result := true;
  1347. end;
  1348. end;
  1349. end
  1350. else
  1351. SetLength(roots, 0);
  1352. end;
  1353. procedure TGLCapsule.Align(const startPoint, endPoint: TVector);
  1354. var
  1355. dir: TAffineVector;
  1356. begin
  1357. AbsolutePosition := startPoint;
  1358. VectorSubtract(endPoint, startPoint, dir);
  1359. if Parent <> nil then
  1360. dir := Parent.AbsoluteToLocal(dir);
  1361. Up.AsAffineVector := dir;
  1362. FHeight := VectorLength(dir);
  1363. Lift(FHeight * 0.5);
  1364. Alignment := caCenter;
  1365. end;
  1366. procedure TGLCapsule.Align(const startObj, endObj: TGLBaseSceneObject);
  1367. begin
  1368. Align(startObj.AbsolutePosition, endObj.AbsolutePosition);
  1369. end;
  1370. procedure TGLCapsule.Align(const startPoint, endPoint: TAffineVector);
  1371. begin
  1372. Align(PointMake(startPoint), PointMake(endPoint));
  1373. end;
  1374. // ------------------
  1375. // ------------------ TGLAnnulus ------------------
  1376. // ------------------
  1377. constructor TGLAnnulus.Create(AOwner: TComponent);
  1378. begin
  1379. inherited Create(AOwner);
  1380. FBottomInnerRadius := 0.3;
  1381. FTopInnerRadius := 0.3;
  1382. FTopRadius := 0.5;
  1383. FParts := [anInnerSides, anOuterSides, anBottom, anTop];
  1384. end;
  1385. procedure TGLAnnulus.SetBottomInnerRadius(const aValue: Single);
  1386. begin
  1387. if aValue <> FBottomInnerRadius then
  1388. begin
  1389. FBottomInnerRadius := aValue;
  1390. StructureChanged;
  1391. end;
  1392. end;
  1393. procedure TGLAnnulus.SetTopRadius(const aValue: Single);
  1394. begin
  1395. if aValue <> FTopRadius then
  1396. begin
  1397. FTopRadius := aValue;
  1398. StructureChanged;
  1399. end;
  1400. end;
  1401. procedure TGLAnnulus.SetTopInnerRadius(const aValue: Single);
  1402. begin
  1403. if aValue <> FTopInnerRadius then
  1404. begin
  1405. FTopInnerRadius := aValue;
  1406. StructureChanged;
  1407. end;
  1408. end;
  1409. procedure TGLAnnulus.SetParts(aValue: TAnnulusParts);
  1410. begin
  1411. if aValue <> FParts then
  1412. begin
  1413. FParts := aValue;
  1414. StructureChanged;
  1415. end;
  1416. end;
  1417. procedure TGLAnnulus.BuildList(var rci: TGLRenderContextInfo);
  1418. var
  1419. quadric: PGLUquadricObj;
  1420. begin
  1421. gl.PushMatrix;
  1422. quadric := gluNewQuadric;
  1423. SetupQuadricParams(quadric);
  1424. gl.Rotatef(-90, 1, 0, 0);
  1425. gl.Translatef(0, 0, -FHeight * 0.5);
  1426. if anOuterSides in FParts then
  1427. gluCylinder(quadric, FBottomRadius, FTopRadius, FHeight, FSlices, FStacks);
  1428. if anTop in FParts then
  1429. begin
  1430. gl.PushMatrix;
  1431. gl.Translatef(0, 0, FHeight);
  1432. gluDisk(quadric, FTopInnerRadius, FTopRadius, FSlices, FLoops);
  1433. gl.PopMatrix;
  1434. end;
  1435. if [anBottom, anInnerSides] * FParts <> [] then
  1436. begin
  1437. // swap quadric orientation because top of a disk is defined as outside
  1438. SetInvertedQuadricOrientation(quadric);
  1439. if anBottom in FParts then
  1440. gluDisk(quadric, FBottomInnerRadius, FBottomRadius, FSlices, FLoops);
  1441. if anInnerSides in FParts then
  1442. gluCylinder(quadric, FBottomInnerRadius, FTopInnerRadius, FHeight,
  1443. FSlices, FStacks);
  1444. end;
  1445. gluDeleteQuadric(quadric);
  1446. gl.PopMatrix;
  1447. end;
  1448. procedure TGLAnnulus.Assign(Source: TPersistent);
  1449. begin
  1450. if Assigned(Source) and (Source is TGLAnnulus) then
  1451. begin
  1452. FParts := TGLAnnulus(Source).FParts;
  1453. FTopRadius := TGLAnnulus(Source).FTopRadius;
  1454. FTopInnerRadius := TGLAnnulus(Source).FTopInnerRadius;
  1455. FBottomRadius := TGLAnnulus(Source).FBottomRadius;
  1456. FBottomInnerRadius := TGLAnnulus(Source).FBottomInnerRadius;
  1457. end;
  1458. inherited Assign(Source);
  1459. end;
  1460. function TGLAnnulus.AxisAlignedDimensionsUnscaled: TVector;
  1461. var
  1462. r, r1: Single;
  1463. begin
  1464. r := Abs(FBottomRadius);
  1465. r1 := Abs(FTopRadius);
  1466. if r1 > r then
  1467. r := r1;
  1468. Result := VectorMake(r, 0.5 * FHeight, r);
  1469. end;
  1470. function TGLAnnulus.RayCastIntersect(const rayStart, rayVector: TVector;
  1471. intersectPoint, intersectNormal: PVector): Boolean;
  1472. const
  1473. cOne: Single = 1;
  1474. var
  1475. locRayStart, locRayVector, ip: TVector;
  1476. poly: array [0 .. 2] of Double;
  1477. t, tr2, invRayVector1: Single;
  1478. tPlaneMin, tPlaneMax: Single;
  1479. tir2, d2: Single;
  1480. Root: Double;
  1481. roots, tmpRoots: TDoubleArray;
  1482. FirstIntersected: Boolean;
  1483. h1, h2, hTop, hBot: Single;
  1484. Draw1, Draw2: Boolean;
  1485. begin
  1486. Result := False;
  1487. FirstIntersected := False;
  1488. SetLength(tmpRoots, 0);
  1489. locRayStart := AbsoluteToLocal(rayStart);
  1490. locRayVector := AbsoluteToLocal(rayVector);
  1491. hTop := Height * 0.5;
  1492. hBot := -hTop;
  1493. if locRayVector.Y < 0 then
  1494. begin // Sort the planes according to the direction of view
  1495. h1 := hTop; // Height of the 1st plane
  1496. h2 := hBot; // Height of the 2nd plane
  1497. Draw1 := (anTop in Parts); // 1st "cap" Must be drawn?
  1498. Draw2 := (anBottom in Parts);
  1499. end
  1500. else
  1501. begin
  1502. h1 := hBot;
  1503. h2 := hTop;
  1504. Draw1 := (anBottom in Parts);
  1505. Draw2 := (anTop in Parts);
  1506. end; // if
  1507. if locRayVector.Y = 0 then
  1508. begin
  1509. // intersect if ray shot through the top/bottom planes
  1510. if (locRayStart.X > hTop) or (locRayStart.X < hBot) then
  1511. Exit;
  1512. tPlaneMin := -1E99;
  1513. tPlaneMax := 1E99;
  1514. end
  1515. else
  1516. begin
  1517. invRayVector1 := cOne / locRayVector.Y;
  1518. tr2 := Sqr(TopRadius);
  1519. tir2 := Sqr(TopInnerRadius);
  1520. FirstIntersected := False;
  1521. // compute intersection with first plane
  1522. t := (h1 - locRayStart.Y) * invRayVector1;
  1523. if (t > 0) and Draw1 then
  1524. begin
  1525. ip.X := locRayStart.X + t * locRayVector.X;
  1526. ip.Z := locRayStart.Z + t * locRayVector.Z;
  1527. d2 := Sqr(ip.X) + Sqr(ip.Z);
  1528. if (d2 <= tr2) and (d2 >= tir2) then
  1529. begin
  1530. // intersect with top plane
  1531. FirstIntersected := true;
  1532. if Assigned(intersectPoint) then
  1533. intersectPoint^ := LocalToAbsolute(VectorMake(ip.X, h1, ip.Z, 1));
  1534. if Assigned(intersectNormal) then
  1535. intersectNormal^ := LocalToAbsolute(YHmgVector);
  1536. Result := true;
  1537. end;
  1538. end;
  1539. tPlaneMin := t;
  1540. tPlaneMax := t;
  1541. // compute intersection with second plane
  1542. t := (h2 - locRayStart.Y) * invRayVector1;
  1543. if (t > 0) and Draw2 then
  1544. begin
  1545. ip.X := locRayStart.X + t * locRayVector.X;
  1546. ip.Z := locRayStart.Z + t * locRayVector.Z;
  1547. d2 := Sqr(ip.X) + Sqr(ip.Z);
  1548. if (t < tPlaneMin) or (not FirstIntersected) then
  1549. begin
  1550. if (d2 <= tr2) and (d2 >= tir2) then
  1551. begin
  1552. // intersect with top plane
  1553. if Assigned(intersectPoint) then
  1554. intersectPoint^ := LocalToAbsolute(VectorMake(ip.X, h2, ip.Z, 1));
  1555. if Assigned(intersectNormal) then
  1556. intersectNormal^ := LocalToAbsolute(VectorNegate(YHmgVector));
  1557. Result := true;
  1558. end;
  1559. end;
  1560. end;
  1561. if t < tPlaneMin then
  1562. begin
  1563. tPlaneMin := t;
  1564. end; // if
  1565. if t > tPlaneMax then
  1566. tPlaneMax := t;
  1567. end;
  1568. try
  1569. SetLength(roots, 4);
  1570. roots[0] := -1;
  1571. roots[1] := -1;
  1572. roots[2] := -1;
  1573. roots[3] := -1; // By default, side is behind rayStart
  1574. // Compute roots for outer cylinder
  1575. if anOuterSides in Parts then
  1576. begin
  1577. // intersect against infinite cylinder, will be cut by tPlaneMine and tPlaneMax
  1578. poly[0] := Sqr(locRayStart.X) + Sqr(locRayStart.Z) - Sqr(TopRadius);
  1579. poly[1] := 2 * (locRayStart.X * locRayVector.X + locRayStart.Z *
  1580. locRayVector.Z);
  1581. poly[2] := Sqr(locRayVector.X) + Sqr(locRayVector.Z);
  1582. tmpRoots := SolveQuadric(@poly);
  1583. // Intersect coordinates on rayVector (rayStart=0)
  1584. if ( High(tmpRoots) >= 0) and // Does root exist?
  1585. ((tmpRoots[0] > tPlaneMin) and not FirstIntersected) and
  1586. // In the annulus and not masked by first cap
  1587. ((tmpRoots[0] < tPlaneMax)) { // In the annulus } then
  1588. roots[0] := tmpRoots[0];
  1589. if ( High(tmpRoots) >= 1) and
  1590. ((tmpRoots[1] > tPlaneMin) and not FirstIntersected) and
  1591. ((tmpRoots[1] < tPlaneMax)) then
  1592. roots[1] := tmpRoots[1];
  1593. end; // if
  1594. // Compute roots for inner cylinder
  1595. if anInnerSides in Parts then
  1596. begin
  1597. // intersect against infinite cylinder
  1598. poly[0] := Sqr(locRayStart.X) +
  1599. Sqr(locRayStart.Z) - Sqr(TopInnerRadius);
  1600. poly[1] := 2 * (locRayStart.X * locRayVector.X +
  1601. locRayStart.Z * locRayVector.Z);
  1602. poly[2] := Sqr(locRayVector.X) + Sqr(locRayVector.Z);
  1603. tmpRoots := SolveQuadric(@poly);
  1604. if ( High(tmpRoots) >= 0) and
  1605. ((tmpRoots[0] > tPlaneMin) and not FirstIntersected) and
  1606. ((tmpRoots[0] < tPlaneMax)) then
  1607. roots[2] := tmpRoots[0];
  1608. if ( High(tmpRoots) >= 1) and
  1609. ((tmpRoots[1] > tPlaneMin) and not FirstIntersected) and
  1610. ((tmpRoots[1] < tPlaneMax)) then
  1611. roots[3] := tmpRoots[1];
  1612. end; // if
  1613. { Find the first intersection point and compute its coordinates and normal }
  1614. if MinPositiveCoef(roots, Root) then
  1615. begin
  1616. t := Root;
  1617. if (t >= tPlaneMin) and (t < tPlaneMax) then
  1618. begin
  1619. if Assigned(intersectPoint) or Assigned(intersectNormal) then
  1620. begin
  1621. ip := VectorCombine(locRayStart, locRayVector, 1, t);
  1622. if Assigned(intersectPoint) then
  1623. intersectPoint^ := LocalToAbsolute(ip);
  1624. if Assigned(intersectNormal) then
  1625. begin
  1626. ip.Y := 0;
  1627. ip.W := 0;
  1628. intersectNormal^ := LocalToAbsolute(ip);
  1629. end;
  1630. end;
  1631. Result := true;
  1632. end;
  1633. end;
  1634. finally
  1635. roots := nil;
  1636. tmpRoots := nil;
  1637. end; // finally
  1638. end;
  1639. // ------------------
  1640. // ------------------ TGLTorus ------------------
  1641. // ------------------
  1642. constructor TGLTorus.Create(AOwner: TComponent);
  1643. begin
  1644. inherited Create(AOwner);
  1645. FRings := 25;
  1646. FSides := 15;
  1647. FMinorRadius := 0.1;
  1648. FMajorRadius := 0.4;
  1649. FStartAngle := 0.0;
  1650. FStopAngle := 360.0;
  1651. FParts := [toSides, toStartDisk, toStopDisk];
  1652. end;
  1653. procedure TGLTorus.BuildList(var rci: TGLRenderContextInfo);
  1654. procedure EmitVertex(ptr: PVertexRec; L1, L2: integer);
  1655. begin
  1656. XGL.TexCoord2fv(@ptr^.TexCoord);
  1657. begin
  1658. gl.Normal3fv(@ptr^.Normal);
  1659. if L1 > -1 then
  1660. gl.VertexAttrib3fv(L1, @ptr.Tangent);
  1661. if L2 > -1 then
  1662. gl.VertexAttrib3fv(L2, @ptr.Binormal);
  1663. gl.Vertex3fv(@ptr^.Position);
  1664. end;
  1665. end;
  1666. var
  1667. i, j: integer;
  1668. Theta, Phi, Theta1, cosPhi, sinPhi, dist: Single;
  1669. cosTheta1, sinTheta1: Single;
  1670. ringDelta, sideDelta: Single;
  1671. ringDir: TAffineVector;
  1672. iFact, jFact: Single;
  1673. pVertex: PVertexRec;
  1674. TanLoc, BinLoc: Integer;
  1675. MeshSize: integer;
  1676. MeshIndex: integer;
  1677. Vertex: TVertexRec;
  1678. begin
  1679. if FMesh = nil then
  1680. begin
  1681. MeshSize := 0;
  1682. MeshIndex := 0;
  1683. if toStartDisk in FParts then
  1684. MeshSize := MeshSize + 1;
  1685. if toStopDisk in FParts then
  1686. MeshSize := MeshSize + 1;
  1687. if toSides in FParts then
  1688. MeshSize := MeshSize + Integer(FRings) + 1;
  1689. SetLength(FMesh, MeshSize);
  1690. // handle texture generation
  1691. ringDelta := ((FStopAngle - FStartAngle) / 360) * c2PI / FRings;
  1692. sideDelta := c2PI / FSides;
  1693. iFact := 1 / FRings;
  1694. jFact := 1 / FSides;
  1695. if toSides in FParts then
  1696. begin
  1697. Theta := DegToRadian(FStartAngle) - ringDelta;
  1698. for i := FRings downto 0 do
  1699. begin
  1700. SetLength(FMesh[i], FSides + 1);
  1701. Theta1 := Theta + ringDelta;
  1702. SinCosine(Theta1, sinTheta1, cosTheta1);
  1703. Phi := 0;
  1704. for j := FSides downto 0 do
  1705. begin
  1706. Phi := Phi + sideDelta;
  1707. SinCosine(Phi, sinPhi, cosPhi);
  1708. dist := FMajorRadius + FMinorRadius * cosPhi;
  1709. FMesh[i][j].Position := Vector3fMake(cosTheta1 * dist,
  1710. -sinTheta1 * dist, FMinorRadius * sinPhi);
  1711. ringDir := FMesh[i][j].Position;
  1712. ringDir.Z := 0.0;
  1713. NormalizeVector(ringDir);
  1714. FMesh[i][j].Normal := Vector3fMake(cosTheta1 * cosPhi,
  1715. -sinTheta1 * cosPhi, sinPhi);
  1716. FMesh[i][j].Tangent := VectorCrossProduct(ZVector, ringDir);
  1717. FMesh[i][j].Binormal := VectorCrossProduct(FMesh[i][j].Normal,
  1718. FMesh[i][j].Tangent);
  1719. FMesh[i][j].TexCoord := Vector2fMake(i * iFact, j * jFact);
  1720. end;
  1721. Theta := Theta1;
  1722. end;
  1723. MeshIndex := FRings + 1;
  1724. end;
  1725. if toStartDisk in FParts then
  1726. begin
  1727. SetLength(FMesh[MeshIndex], FSides + 1);
  1728. Theta1 := DegToRadian(FStartAngle);
  1729. SinCosine(Theta1, sinTheta1, cosTheta1);
  1730. if toSides in FParts then
  1731. begin
  1732. for j := FSides downto 0 do
  1733. begin
  1734. FMesh[MeshIndex][j].Position := FMesh[MeshIndex - 1][j].Position;
  1735. FMesh[MeshIndex][j].Normal := FMesh[MeshIndex - 1][j].Tangent;
  1736. FMesh[MeshIndex][j].Tangent := FMesh[MeshIndex - 1][j].Position;
  1737. FMesh[MeshIndex][j].Tangent.Z := 0;
  1738. FMesh[MeshIndex][j].Binormal := ZVector;
  1739. FMesh[MeshIndex][j].TexCoord := FMesh[MeshIndex - 1][j].TexCoord;
  1740. FMesh[MeshIndex][j].TexCoord.X := 0;
  1741. end;
  1742. end
  1743. else
  1744. begin
  1745. Phi := 0;
  1746. for j := FSides downto 0 do
  1747. begin
  1748. Phi := Phi + sideDelta;
  1749. SinCosine(Phi, sinPhi, cosPhi);
  1750. dist := FMajorRadius + FMinorRadius * cosPhi;
  1751. FMesh[MeshIndex][j].Position := Vector3fMake(cosTheta1 * dist,
  1752. -sinTheta1 * dist, FMinorRadius * sinPhi);
  1753. ringDir := FMesh[MeshIndex][j].Position;
  1754. ringDir.Z := 0.0;
  1755. NormalizeVector(ringDir);
  1756. FMesh[MeshIndex][j].Normal := VectorCrossProduct(ZVector, ringDir);
  1757. FMesh[MeshIndex][j].Tangent := ringDir;
  1758. FMesh[MeshIndex][j].Binormal := ZVector;
  1759. FMesh[MeshIndex][j].TexCoord := Vector2fMake(0, j * jFact);
  1760. end;
  1761. end;
  1762. Vertex.Position := Vector3fMake(cosTheta1 * FMajorRadius,
  1763. -sinTheta1 * FMajorRadius, 0);
  1764. Vertex.Normal := FMesh[MeshIndex][0].Normal;
  1765. Vertex.Tangent := FMesh[MeshIndex][0].Tangent;
  1766. Vertex.Binormal := FMesh[MeshIndex][0].Binormal;
  1767. Vertex.TexCoord := Vector2fMake(1, 1);
  1768. MeshIndex := MeshIndex + 1;
  1769. end;
  1770. if toStopDisk in FParts then
  1771. begin
  1772. SetLength(FMesh[MeshIndex], FSides + 1);
  1773. Theta1 := DegToRadian(FStopAngle);
  1774. SinCosine(Theta1, sinTheta1, cosTheta1);
  1775. if toSides in FParts then
  1776. begin
  1777. for j := FSides downto 0 do
  1778. begin
  1779. FMesh[MeshIndex][j].Position := FMesh[0][j].Position;
  1780. FMesh[MeshIndex][j].Normal := VectorNegate(FMesh[0][j].Tangent);
  1781. FMesh[MeshIndex][j].Tangent := FMesh[0][j].Position;
  1782. FMesh[MeshIndex][j].Tangent.Z := 0;
  1783. FMesh[MeshIndex][j].Binormal := VectorNegate(ZVector);
  1784. FMesh[MeshIndex][j].TexCoord := FMesh[0][j].TexCoord;
  1785. FMesh[MeshIndex][j].TexCoord.X := 1;
  1786. end;
  1787. end
  1788. else
  1789. begin
  1790. Phi := 0;
  1791. for j := FSides downto 0 do
  1792. begin
  1793. Phi := Phi + sideDelta;
  1794. SinCosine(Phi, sinPhi, cosPhi);
  1795. dist := FMajorRadius + FMinorRadius * cosPhi;
  1796. FMesh[MeshIndex][j].Position := Vector3fMake(cosTheta1 * dist,
  1797. -sinTheta1 * dist, FMinorRadius * sinPhi);
  1798. ringDir := FMesh[MeshIndex][j].Position;
  1799. ringDir.Z := 0.0;
  1800. NormalizeVector(ringDir);
  1801. FMesh[MeshIndex][j].Normal := VectorCrossProduct(ringDir, ZVector);
  1802. FMesh[MeshIndex][j].Tangent := ringDir;
  1803. FMesh[MeshIndex][j].Binormal := VectorNegate(ZVector);
  1804. FMesh[MeshIndex][j].TexCoord := Vector2fMake(1, j * jFact);
  1805. end;
  1806. end;
  1807. Vertex.Position := Vector3fMake(cosTheta1 * FMajorRadius,
  1808. -sinTheta1 * FMajorRadius, 0);
  1809. Vertex.Normal := FMesh[MeshIndex][0].Normal;
  1810. Vertex.Tangent := FMesh[MeshIndex][0].Tangent;
  1811. Vertex.Binormal := FMesh[MeshIndex][0].Binormal;
  1812. Vertex.TexCoord := Vector2fMake(0, 0);
  1813. end;
  1814. end;
  1815. begin
  1816. if GL.ARB_shader_objects and (rci.GLStates.CurrentProgram > 0) then
  1817. begin
  1818. TanLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  1819. PAnsiChar(TangentAttributeName));
  1820. BinLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  1821. PAnsiChar(BinormalAttributeName));
  1822. end
  1823. else
  1824. begin
  1825. TanLoc := -1;
  1826. BinLoc := TanLoc;
  1827. end;
  1828. MeshIndex := 0;
  1829. if toSides in FParts then
  1830. begin
  1831. gl.Begin_(GL_TRIANGLES);
  1832. for i := FRings - 1 downto 0 do
  1833. for j := FSides - 1 downto 0 do
  1834. begin
  1835. pVertex := @FMesh[i][j];
  1836. EmitVertex(pVertex, TanLoc, BinLoc);
  1837. pVertex := @FMesh[i][j + 1];
  1838. EmitVertex(pVertex, TanLoc, BinLoc);
  1839. pVertex := @FMesh[i + 1][j];
  1840. EmitVertex(pVertex, TanLoc, BinLoc);
  1841. pVertex := @FMesh[i + 1][j + 1];
  1842. EmitVertex(pVertex, TanLoc, BinLoc);
  1843. pVertex := @FMesh[i + 1][j];
  1844. EmitVertex(pVertex, TanLoc, BinLoc);
  1845. pVertex := @FMesh[i][j + 1];
  1846. EmitVertex(pVertex, TanLoc, BinLoc);
  1847. end;
  1848. gl.End_;
  1849. MeshIndex := FRings + 1;
  1850. end;
  1851. if toStartDisk in FParts then
  1852. begin
  1853. gl.Begin_(GL_TRIANGLE_FAN);
  1854. pVertex := @Vertex;
  1855. EmitVertex(pVertex, TanLoc, BinLoc);
  1856. for j := 0 to FSides do
  1857. begin
  1858. pVertex := @FMesh[MeshIndex][j];
  1859. EmitVertex(pVertex, TanLoc, BinLoc);
  1860. end;
  1861. gl.End_;
  1862. MeshIndex := MeshIndex + 1;
  1863. end;
  1864. if toStopDisk in FParts then
  1865. begin
  1866. gl.Begin_(GL_TRIANGLE_FAN);
  1867. pVertex := @Vertex;
  1868. EmitVertex(pVertex, TanLoc, BinLoc);
  1869. for j := FSides downto 0 do
  1870. begin
  1871. pVertex := @FMesh[MeshIndex][j];
  1872. EmitVertex(pVertex, TanLoc, BinLoc);
  1873. end;
  1874. gl.End_;
  1875. end;
  1876. end;
  1877. end;
  1878. procedure TGLTorus.SetMajorRadius(const aValue: Single);
  1879. begin
  1880. if FMajorRadius <> aValue then
  1881. begin
  1882. FMajorRadius := aValue;
  1883. FMesh := nil;
  1884. StructureChanged;
  1885. end;
  1886. end;
  1887. procedure TGLTorus.SetMinorRadius(const aValue: Single);
  1888. begin
  1889. if FMinorRadius <> aValue then
  1890. begin
  1891. FMinorRadius := aValue;
  1892. FMesh := nil;
  1893. StructureChanged;
  1894. end;
  1895. end;
  1896. procedure TGLTorus.SetRings(aValue: Cardinal);
  1897. begin
  1898. if FRings <> aValue then
  1899. begin
  1900. FRings := aValue;
  1901. if FRings < 2 then
  1902. FRings := 2;
  1903. FMesh := nil;
  1904. StructureChanged;
  1905. end;
  1906. end;
  1907. procedure TGLTorus.SetSides(aValue: Cardinal);
  1908. begin
  1909. if FSides <> aValue then
  1910. begin
  1911. FSides := aValue;
  1912. if FSides < 3 then
  1913. FSides := 3;
  1914. FMesh := nil;
  1915. StructureChanged;
  1916. end;
  1917. end;
  1918. procedure TGLTorus.SetStartAngle(const aValue: Single);
  1919. begin
  1920. if FStartAngle <> aValue then
  1921. begin
  1922. FStartAngle := aValue;
  1923. FMesh := nil;
  1924. StructureChanged;
  1925. end;
  1926. end;
  1927. procedure TGLTorus.SetStopAngle(const aValue: Single);
  1928. begin
  1929. if FStopAngle <> aValue then
  1930. begin
  1931. FStopAngle := aValue;
  1932. FMesh := nil;
  1933. StructureChanged;
  1934. end;
  1935. end;
  1936. procedure TGLTorus.SetParts(aValue: TTorusParts);
  1937. begin
  1938. if aValue <> FParts then
  1939. begin
  1940. FParts := aValue;
  1941. StructureChanged;
  1942. end;
  1943. end;
  1944. function TGLTorus.AxisAlignedDimensionsUnscaled: TVector;
  1945. var
  1946. r, r1: Single;
  1947. begin
  1948. r := Abs(FMajorRadius);
  1949. r1 := Abs(FMinorRadius);
  1950. Result := VectorMake(r + r1, r + r1, r1); // Danb
  1951. end;
  1952. function TGLTorus.RayCastIntersect(const rayStart, rayVector: TVector;
  1953. intersectPoint: PVector = nil; intersectNormal: PVector = nil): Boolean;
  1954. var
  1955. i: integer;
  1956. fRo2, fRi2, fDE, fVal, r, nearest: Double;
  1957. polynom: array [0 .. 4] of Double;
  1958. polyRoots: TDoubleArray;
  1959. localStart, localVector: TVector;
  1960. vi, vc: TVector;
  1961. begin
  1962. // compute coefficients of quartic polynomial
  1963. fRo2 := Sqr(MajorRadius);
  1964. fRi2 := Sqr(MinorRadius);
  1965. localStart := AbsoluteToLocal(rayStart);
  1966. localVector := AbsoluteToLocal(rayVector);
  1967. NormalizeVector(localVector);
  1968. fDE := VectorDotProduct(localStart, localVector);
  1969. fVal := VectorNorm(localStart) - (fRo2 + fRi2);
  1970. polynom[0] := Sqr(fVal) - 4.0 * fRo2 * (fRi2 - Sqr(localStart.Z));
  1971. polynom[1] := 4.0 * fDE * fVal + 8.0 * fRo2 * localVector.Z * localStart.Z;
  1972. polynom[2] := 2.0 * fVal + 4.0 * Sqr(fDE) + 4.0 * fRo2 * Sqr(localVector.Z);
  1973. polynom[3] := 4.0 * fDE;
  1974. polynom[4] := 1;
  1975. // solve the quartic
  1976. polyRoots := SolveQuartic(@polynom[0]);
  1977. // search for closest point
  1978. Result := (Length(polyRoots) > 0);
  1979. if Result then
  1980. begin
  1981. nearest := 1E20;
  1982. for i := 0 to High(polyRoots) do
  1983. begin
  1984. r := polyRoots[i];
  1985. if (r > 0) and (r < nearest) then
  1986. begin
  1987. nearest := r;
  1988. Result := true;
  1989. end;
  1990. end;
  1991. vi := VectorCombine(localStart, localVector, 1, nearest);
  1992. if Assigned(intersectPoint) then
  1993. SetVector(intersectPoint^, LocalToAbsolute(vi));
  1994. if Assigned(intersectNormal) then
  1995. begin
  1996. // project vi on local torus plane
  1997. vc.X := vi.X;
  1998. vc.Y := vi.Y;
  1999. vc.Z := 0;
  2000. // project vc on MajorRadius circle
  2001. ScaleVector(vc, MajorRadius / (VectorLength(vc) + 0.000001));
  2002. // calculate circle to intersect vector (gives normal);
  2003. SubtractVector(vi, vc);
  2004. // return to absolute coordinates and normalize
  2005. vi.W := 0;
  2006. SetVector(intersectNormal^, LocalToAbsolute(vi));
  2007. end;
  2008. end;
  2009. end;
  2010. // ------------------
  2011. // ------------------ TGLArrowLine ------------------
  2012. // ------------------
  2013. constructor TGLArrowLine.Create(AOwner: TComponent);
  2014. begin
  2015. inherited;
  2016. FTopRadius := 0.1;
  2017. BottomRadius := 0.1;
  2018. fTopArrowHeadRadius := 0.2;
  2019. fTopArrowHeadHeight := 0.5;
  2020. fBottomArrowHeadRadius := 0.2;
  2021. fBottomArrowHeadHeight := 0.5;
  2022. FHeadStackingStyle := ahssStacked;
  2023. { by default there is not much point having the top of the line (cylinder)
  2024. showing as it is coincidental with the Toparrowhead bottom.
  2025. Note I've defaulted to "vector" type arrows (arrow head on top only }
  2026. FParts := [alLine, alTopArrow];
  2027. end;
  2028. procedure TGLArrowLine.SetTopRadius(const aValue: Single);
  2029. begin
  2030. if aValue <> FTopRadius then
  2031. begin
  2032. FTopRadius := aValue;
  2033. StructureChanged;
  2034. end;
  2035. end;
  2036. procedure TGLArrowLine.SetTopArrowHeadHeight(const aValue: Single);
  2037. begin
  2038. if aValue <> fTopArrowHeadHeight then
  2039. begin
  2040. fTopArrowHeadHeight := aValue;
  2041. StructureChanged;
  2042. end;
  2043. end;
  2044. procedure TGLArrowLine.SetTopArrowHeadRadius(const aValue: Single);
  2045. begin
  2046. if aValue <> fTopArrowHeadRadius then
  2047. begin
  2048. fTopArrowHeadRadius := aValue;
  2049. StructureChanged;
  2050. end;
  2051. end;
  2052. procedure TGLArrowLine.SetBottomArrowHeadHeight(const aValue: Single);
  2053. begin
  2054. if aValue <> fBottomArrowHeadHeight then
  2055. begin
  2056. fBottomArrowHeadHeight := aValue;
  2057. StructureChanged;
  2058. end;
  2059. end;
  2060. procedure TGLArrowLine.SetBottomArrowHeadRadius(const aValue: Single);
  2061. begin
  2062. if aValue <> fBottomArrowHeadRadius then
  2063. begin
  2064. fBottomArrowHeadRadius := aValue;
  2065. StructureChanged;
  2066. end;
  2067. end;
  2068. procedure TGLArrowLine.SetParts(aValue: TArrowLineParts);
  2069. begin
  2070. if aValue <> FParts then
  2071. begin
  2072. FParts := aValue;
  2073. StructureChanged;
  2074. end;
  2075. end;
  2076. procedure TGLArrowLine.SetHeadStackingStyle(const val: TGLArrowHeadStyle);
  2077. begin
  2078. if val <> FHeadStackingStyle then
  2079. begin
  2080. FHeadStackingStyle := val;
  2081. StructureChanged;
  2082. end;
  2083. end;
  2084. procedure TGLArrowLine.BuildList(var rci: TGLRenderContextInfo);
  2085. var
  2086. quadric: PGLUquadricObj;
  2087. cylHeight, cylOffset, headInfluence: Single;
  2088. begin
  2089. case HeadStackingStyle of
  2090. ahssCentered:
  2091. headInfluence := 0.5;
  2092. ahssIncluded:
  2093. headInfluence := 1;
  2094. else // ahssStacked
  2095. headInfluence := 0;
  2096. end;
  2097. cylHeight := Height;
  2098. cylOffset := -FHeight * 0.5;
  2099. // create a new quadric
  2100. quadric := gluNewQuadric;
  2101. SetupQuadricParams(quadric);
  2102. // does the top arrow part - the cone
  2103. if alTopArrow in Parts then
  2104. begin
  2105. cylHeight := cylHeight - TopArrowHeadHeight * headInfluence;
  2106. gl.PushMatrix;
  2107. gl.Translatef(0, 0, Height * 0.5 - TopArrowHeadHeight * headInfluence);
  2108. gluCylinder(quadric, fTopArrowHeadRadius, 0, fTopArrowHeadHeight,
  2109. Slices, Stacks);
  2110. // top of a disk is defined as outside
  2111. SetInvertedQuadricOrientation(quadric);
  2112. if alLine in Parts then
  2113. gluDisk(quadric, FTopRadius, fTopArrowHeadRadius, Slices, FLoops)
  2114. else
  2115. gluDisk(quadric, 0, fTopArrowHeadRadius, Slices, FLoops);
  2116. gl.PopMatrix;
  2117. end;
  2118. // does the bottom arrow part - another cone
  2119. if alBottomArrow in Parts then
  2120. begin
  2121. cylHeight := cylHeight - BottomArrowHeadHeight * headInfluence;
  2122. cylOffset := cylOffset + BottomArrowHeadHeight * headInfluence;
  2123. gl.PushMatrix;
  2124. // make the bottom arrow point in the other direction
  2125. gl.Rotatef(180, 1, 0, 0);
  2126. gl.Translatef(0, 0, Height * 0.5 - BottomArrowHeadHeight * headInfluence);
  2127. SetNormalQuadricOrientation(quadric);
  2128. gluCylinder(quadric, fBottomArrowHeadRadius, 0, fBottomArrowHeadHeight,
  2129. Slices, Stacks);
  2130. // top of a disk is defined as outside
  2131. SetInvertedQuadricOrientation(quadric);
  2132. if alLine in Parts then
  2133. gluDisk(quadric, FBottomRadius, fBottomArrowHeadRadius, Slices, FLoops)
  2134. else
  2135. gluDisk(quadric, 0, fBottomArrowHeadRadius, Slices, FLoops);
  2136. gl.PopMatrix;
  2137. end;
  2138. // does the cylinder that makes the line
  2139. if (cylHeight > 0) and (alLine in Parts) then
  2140. begin
  2141. gl.PushMatrix;
  2142. gl.Translatef(0, 0, cylOffset);
  2143. SetNormalQuadricOrientation(quadric);
  2144. gluCylinder(quadric, FBottomRadius, FTopRadius, cylHeight, FSlices,
  2145. FStacks);
  2146. if not(alTopArrow in Parts) then
  2147. begin
  2148. gl.PushMatrix;
  2149. gl.Translatef(0, 0, cylHeight);
  2150. gluDisk(quadric, 0, FTopRadius, FSlices, FLoops);
  2151. gl.PopMatrix;
  2152. end;
  2153. if not(alBottomArrow in Parts) then
  2154. begin
  2155. // swap quadric orientation because top of a disk is defined as outside
  2156. SetInvertedQuadricOrientation(quadric);
  2157. gluDisk(quadric, 0, FBottomRadius, FSlices, FLoops);
  2158. end;
  2159. gl.PopMatrix;
  2160. end;
  2161. gluDeleteQuadric(quadric);
  2162. end;
  2163. procedure TGLArrowLine.Assign(Source: TPersistent);
  2164. begin
  2165. if Assigned(Source) and (Source is TGLArrowLine) then
  2166. begin
  2167. FParts := TGLArrowLine(Source).FParts;
  2168. FTopRadius := TGLArrowLine(Source).FTopRadius;
  2169. fTopArrowHeadHeight := TGLArrowLine(Source).fTopArrowHeadHeight;
  2170. fTopArrowHeadRadius := TGLArrowLine(Source).fTopArrowHeadRadius;
  2171. fBottomArrowHeadHeight := TGLArrowLine(Source).fBottomArrowHeadHeight;
  2172. fBottomArrowHeadRadius := TGLArrowLine(Source).fBottomArrowHeadRadius;
  2173. FHeadStackingStyle := TGLArrowLine(Source).FHeadStackingStyle;
  2174. end;
  2175. inherited Assign(Source);
  2176. end;
  2177. // ------------------
  2178. // ------------------ TGLArrowArc ------------------
  2179. // ------------------
  2180. constructor TGLArrowArc.Create(AOwner: TComponent);
  2181. begin
  2182. inherited;
  2183. FStacks := 16;
  2184. fArcRadius := 0.5;
  2185. FStartAngle := 0;
  2186. FStopAngle := 360;
  2187. FTopRadius := 0.1;
  2188. BottomRadius := 0.1;
  2189. fTopArrowHeadRadius := 0.2;
  2190. fTopArrowHeadHeight := 0.5;
  2191. fBottomArrowHeadRadius := 0.2;
  2192. fBottomArrowHeadHeight := 0.5;
  2193. FHeadStackingStyle := ahssStacked;
  2194. FParts := [aaArc, aaTopArrow];
  2195. end;
  2196. procedure TGLArrowArc.SetArcRadius(const aValue: Single);
  2197. begin
  2198. if fArcRadius <> aValue then
  2199. begin
  2200. fArcRadius := aValue;
  2201. FMesh := nil;
  2202. StructureChanged;
  2203. end;
  2204. end;
  2205. procedure TGLArrowArc.SetStartAngle(const aValue: Single);
  2206. begin
  2207. if FStartAngle <> aValue then
  2208. begin
  2209. FStartAngle := aValue;
  2210. FMesh := nil;
  2211. StructureChanged;
  2212. end;
  2213. end;
  2214. procedure TGLArrowArc.SetStopAngle(const aValue: Single);
  2215. begin
  2216. if FStopAngle <> aValue then
  2217. begin
  2218. FStopAngle := aValue;
  2219. FMesh := nil;
  2220. StructureChanged;
  2221. end;
  2222. end;
  2223. procedure TGLArrowArc.SetTopRadius(const aValue: Single);
  2224. begin
  2225. if aValue <> FTopRadius then
  2226. begin
  2227. FTopRadius := aValue;
  2228. FMesh := nil;
  2229. StructureChanged;
  2230. end;
  2231. end;
  2232. procedure TGLArrowArc.SetTopArrowHeadHeight(const aValue: Single);
  2233. begin
  2234. if aValue <> fTopArrowHeadHeight then
  2235. begin
  2236. fTopArrowHeadHeight := aValue;
  2237. FMesh := nil;
  2238. StructureChanged;
  2239. end;
  2240. end;
  2241. procedure TGLArrowArc.SetTopArrowHeadRadius(const aValue: Single);
  2242. begin
  2243. if aValue <> fTopArrowHeadRadius then
  2244. begin
  2245. fTopArrowHeadRadius := aValue;
  2246. FMesh := nil;
  2247. StructureChanged;
  2248. end;
  2249. end;
  2250. procedure TGLArrowArc.SetBottomArrowHeadHeight(const aValue: Single);
  2251. begin
  2252. if aValue <> fBottomArrowHeadHeight then
  2253. begin
  2254. fBottomArrowHeadHeight := aValue;
  2255. FMesh := nil;
  2256. StructureChanged;
  2257. end;
  2258. end;
  2259. procedure TGLArrowArc.SetBottomArrowHeadRadius(const aValue: Single);
  2260. begin
  2261. if aValue <> fBottomArrowHeadRadius then
  2262. begin
  2263. fBottomArrowHeadRadius := aValue;
  2264. FMesh := nil;
  2265. StructureChanged;
  2266. end;
  2267. end;
  2268. procedure TGLArrowArc.SetParts(aValue: TGLArrowArcPart);
  2269. begin
  2270. if aValue <> FParts then
  2271. begin
  2272. FParts := aValue;
  2273. FMesh := nil;
  2274. StructureChanged;
  2275. end;
  2276. end;
  2277. procedure TGLArrowArc.SetHeadStackingStyle(const val: TGLArrowHeadStyle);
  2278. begin
  2279. if val <> FHeadStackingStyle then
  2280. begin
  2281. FHeadStackingStyle := val;
  2282. FMesh := nil;
  2283. StructureChanged;
  2284. end;
  2285. end;
  2286. procedure TGLArrowArc.BuildList(var rci: TGLRenderContextInfo);
  2287. procedure EmitVertex(ptr: PVertexRec; L1, L2: integer);
  2288. begin
  2289. XGL.TexCoord2fv(@ptr^.TexCoord);
  2290. gl.Normal3fv(@ptr^.Normal);
  2291. if L1 > -1 then
  2292. gl.VertexAttrib3fv(L1, @ptr.Tangent);
  2293. if L2 > -1 then
  2294. gl.VertexAttrib3fv(L2, @ptr.Binormal);
  2295. gl.Vertex3fv(@ptr^.Position);
  2296. end;
  2297. var
  2298. i, j: integer;
  2299. Theta, Phi, Theta1, cosPhi, sinPhi, dist: Single;
  2300. cosTheta1, sinTheta1: Single;
  2301. ringDelta, sideDelta: Single;
  2302. ringDir: TAffineVector;
  2303. iFact, jFact: Single;
  2304. pVertex: PVertexRec;
  2305. TanLoc, BinLoc: Integer;
  2306. MeshSize: integer;
  2307. MeshIndex: integer;
  2308. ConeCenter: TVertexRec;
  2309. StartOffset, StopOffset: Single;
  2310. begin
  2311. if FMesh = nil then
  2312. begin
  2313. MeshIndex := 0;
  2314. MeshSize := 0;
  2315. // Check Parts
  2316. if aaArc in FParts then
  2317. MeshSize := MeshSize + FStacks + 1;
  2318. if aaTopArrow in FParts then
  2319. MeshSize := MeshSize + 3
  2320. else
  2321. MeshSize := MeshSize + 1;
  2322. if aaBottomArrow in FParts then
  2323. MeshSize := MeshSize + 3
  2324. else
  2325. MeshSize := MeshSize + 1;
  2326. // Allocate Mesh
  2327. SetLength(FMesh, MeshSize);
  2328. case FHeadStackingStyle of
  2329. ahssStacked:
  2330. begin
  2331. StartOffset := 0;
  2332. StopOffset := 0;
  2333. end;
  2334. ahssCentered:
  2335. begin
  2336. if aaBottomArrow in Parts then
  2337. StartOffset :=
  2338. RadToDeg(ArcTan(0.5 * fBottomArrowHeadHeight / fArcRadius))
  2339. else
  2340. StartOffset :=0;
  2341. if aaTopArrow in Parts then
  2342. StopOffset :=
  2343. RadToDeg(ArcTan(0.5 * fTopArrowHeadHeight / fArcRadius))
  2344. else
  2345. StopOffset :=0;
  2346. end ;
  2347. ahssIncluded:
  2348. begin
  2349. if aaBottomArrow in Parts then
  2350. StartOffset := RadToDeg(ArcTan(fBottomArrowHeadHeight / fArcRadius))
  2351. else
  2352. StartOffset :=0;
  2353. if aaTopArrow in Parts then
  2354. StopOffset := RadToDeg(ArcTan(fTopArrowHeadHeight / fArcRadius))
  2355. else
  2356. StopOffset :=0;
  2357. end ;
  2358. else
  2359. StartOffset := 0;
  2360. StopOffset := 0;
  2361. end;
  2362. // handle texture generation
  2363. ringDelta := (((FStopAngle - StopOffset) - (FStartAngle + StartOffset)) /
  2364. 360) * c2PI / FStacks;
  2365. sideDelta := c2PI / FSlices;
  2366. iFact := 1 / FStacks;
  2367. jFact := 1 / FSlices;
  2368. if aaArc in FParts then
  2369. begin
  2370. Theta := DegToRadian(FStartAngle + StartOffset) - ringDelta;
  2371. for i := FStacks downto 0 do
  2372. begin
  2373. SetLength(FMesh[i], FSlices + 1);
  2374. Theta1 := Theta + ringDelta;
  2375. SinCosine(Theta1, sinTheta1, cosTheta1);
  2376. Phi := 0;
  2377. for j := FSlices downto 0 do
  2378. begin
  2379. Phi := Phi + sideDelta;
  2380. SinCosine(Phi, sinPhi, cosPhi);
  2381. dist := fArcRadius + Lerp(FTopRadius, FBottomRadius, i * iFact) * cosPhi;
  2382. FMesh[i][j].Position := Vector3fMake(cosTheta1 * dist,
  2383. -sinTheta1 * dist, Lerp(FTopRadius, FBottomRadius, i * iFact) * sinPhi);
  2384. ringDir := FMesh[i][j].Position;
  2385. ringDir.Z := 0.0;
  2386. NormalizeVector(ringDir);
  2387. FMesh[i][j].Normal := Vector3fMake(cosTheta1 * cosPhi,
  2388. -sinTheta1 * cosPhi, sinPhi);
  2389. FMesh[i][j].Tangent := VectorCrossProduct(ZVector, ringDir);
  2390. FMesh[i][j].Binormal := VectorCrossProduct(FMesh[i][j].Normal,
  2391. FMesh[i][j].Tangent);
  2392. FMesh[i][j].TexCoord := Vector2fMake(i * iFact, j * jFact);
  2393. end;
  2394. Theta := Theta1;
  2395. end;
  2396. MeshIndex := FStacks + 1;
  2397. begin
  2398. if GL.ARB_shader_objects and (rci.GLStates.CurrentProgram > 0) then
  2399. begin
  2400. TanLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2401. PAnsiChar(TangentAttributeName));
  2402. BinLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2403. PAnsiChar(BinormalAttributeName));
  2404. end
  2405. else
  2406. begin
  2407. TanLoc := -1;
  2408. BinLoc := TanLoc;
  2409. end;
  2410. gl.Begin_(GL_TRIANGLES);
  2411. for i := FStacks - 1 downto 0 do
  2412. for j := FSlices - 1 downto 0 do
  2413. begin
  2414. pVertex := @FMesh[i][j];
  2415. EmitVertex(pVertex, TanLoc, BinLoc);
  2416. pVertex := @FMesh[i][j + 1];
  2417. EmitVertex(pVertex, TanLoc, BinLoc);
  2418. pVertex := @FMesh[i + 1][j];
  2419. EmitVertex(pVertex, TanLoc, BinLoc);
  2420. pVertex := @FMesh[i + 1][j + 1];
  2421. EmitVertex(pVertex, TanLoc, BinLoc);
  2422. pVertex := @FMesh[i + 1][j];
  2423. EmitVertex(pVertex, TanLoc, BinLoc);
  2424. pVertex := @FMesh[i][j + 1];
  2425. EmitVertex(pVertex, TanLoc, BinLoc);
  2426. end;
  2427. gl.End_;
  2428. end;
  2429. end;
  2430. // Build Arrow or start cap
  2431. if aaBottomArrow in FParts then
  2432. begin
  2433. SetLength(FMesh[MeshIndex], FSlices + 1);
  2434. SetLength(FMesh[MeshIndex + 1], FSlices + 1);
  2435. SetLength(FMesh[MeshIndex + 2], FSlices + 1);
  2436. Theta1 := DegToRadian(FStartAngle + StartOffset);
  2437. SinCosine(Theta1, sinTheta1, cosTheta1);
  2438. ConeCenter.Position := Vector3fMake(cosTheta1 * fArcRadius,
  2439. -sinTheta1 * fArcRadius, 0);
  2440. Phi := 0;
  2441. for j := FSlices downto 0 do
  2442. begin
  2443. Phi := Phi + sideDelta;
  2444. SinCosine(Phi, sinPhi, cosPhi);
  2445. dist := fArcRadius + fBottomArrowHeadRadius * cosPhi;
  2446. // Cap
  2447. FMesh[MeshIndex][J].Position := Vector3fMake(cosTheta1 * dist,
  2448. -sinTheta1 * dist, fBottomArrowHeadRadius * sinPhi);
  2449. ringDir := FMesh[MeshIndex][j].Position;
  2450. ringDir.Z := 0.0;
  2451. NormalizeVector(ringDir);
  2452. FMesh[MeshIndex][j].Normal := VectorCrossProduct(ringDir, ZVector);
  2453. FMesh[MeshIndex][j].Tangent := ringDir;
  2454. FMesh[MeshIndex][j].Binormal := ZVector;
  2455. FMesh[MeshIndex][j].TexCoord := Vector2fMake(1, j * jFact);
  2456. // Cone
  2457. FMesh[MeshIndex+1][j].Position := Vector3fMake(cosTheta1 * dist,
  2458. -sinTheta1 * dist, fBottomArrowHeadRadius * sinPhi);
  2459. FMesh[MeshIndex+2][j].Position := VectorAdd(ConeCenter.Position,
  2460. Vector3fMake(sinTheta1 * fBottomArrowHeadHeight,
  2461. cosTheta1 * fBottomArrowHeadHeight, 0));
  2462. FMesh[MeshIndex + 1][j].Tangent :=
  2463. VectorNormalize(VectorSubtract(FMesh[MeshIndex + 1][j].Position,
  2464. FMesh[MeshIndex + 2][j].Position));
  2465. FMesh[MeshIndex + 2][j].Tangent := FMesh[MeshIndex + 1][j].Tangent;
  2466. FMesh[MeshIndex + 1][j].Binormal := Vector3fMake(cosTheta1 * -sinPhi,
  2467. sinTheta1 * sinPhi, cosPhi);
  2468. FMesh[MeshIndex + 2][j].Binormal := FMesh[MeshIndex + 1][j].Binormal;
  2469. FMesh[MeshIndex + 1][j].Normal :=
  2470. VectorCrossProduct(FMesh[MeshIndex + 1][j].Binormal,
  2471. FMesh[MeshIndex + 1][j].Tangent);
  2472. FMesh[MeshIndex + 2][j].Normal := FMesh[MeshIndex + 1][j].Normal;
  2473. FMesh[MeshIndex + 1][j].TexCoord := Vector2fMake(0, j * jFact);
  2474. FMesh[MeshIndex + 2][j].TexCoord := Vector2fMake(1, j * jFact);
  2475. end;
  2476. ConeCenter.Normal := FMesh[MeshIndex][0].Normal;
  2477. ConeCenter.Tangent := FMesh[MeshIndex][0].Tangent;
  2478. ConeCenter.Binormal := FMesh[MeshIndex][0].Binormal;
  2479. ConeCenter.TexCoord := Vector2fMake(0, 0);
  2480. begin
  2481. if GL.ARB_shader_objects and (rci.GLStates.CurrentProgram > 0) then
  2482. begin
  2483. TanLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2484. PAnsiChar(TangentAttributeName));
  2485. BinLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2486. PAnsiChar(BinormalAttributeName));
  2487. end
  2488. else
  2489. begin
  2490. TanLoc := -1;
  2491. BinLoc := TanLoc;
  2492. end;
  2493. gl.Begin_(GL_TRIANGLE_FAN);
  2494. pVertex := @ConeCenter;
  2495. EmitVertex(pVertex, TanLoc, BinLoc);
  2496. for j := FSlices downto 0 do
  2497. begin
  2498. pVertex := @FMesh[MeshIndex][j];
  2499. EmitVertex(pVertex, TanLoc, BinLoc);
  2500. end;
  2501. gl.End_;
  2502. gl.Begin_(GL_TRIANGLES);
  2503. for j := FSlices - 1 downto 0 do
  2504. begin
  2505. pVertex := @FMesh[MeshIndex + 1][j];
  2506. EmitVertex(pVertex, TanLoc, BinLoc);
  2507. pVertex := @FMesh[MeshIndex + 1][j + 1];
  2508. EmitVertex(pVertex, TanLoc, BinLoc);
  2509. pVertex := @FMesh[MeshIndex + 2][j];
  2510. EmitVertex(pVertex, TanLoc, BinLoc);
  2511. pVertex := @FMesh[MeshIndex + 2][j + 1];
  2512. EmitVertex(pVertex, TanLoc, BinLoc);
  2513. pVertex := @FMesh[MeshIndex + 2][j];
  2514. EmitVertex(pVertex, TanLoc, BinLoc);
  2515. pVertex := @FMesh[MeshIndex + 1][j + 1];
  2516. EmitVertex(pVertex, TanLoc, BinLoc);
  2517. end;
  2518. gl.End_;
  2519. end;
  2520. MeshIndex := MeshIndex + 3;
  2521. end
  2522. else
  2523. begin
  2524. SetLength(FMesh[MeshIndex], FSlices + 1);
  2525. Theta1 := DegToRadian(FStartAngle);
  2526. SinCosine(Theta1, sinTheta1, cosTheta1);
  2527. Phi := 0;
  2528. for j := FSlices downto 0 do
  2529. begin
  2530. Phi := Phi + sideDelta;
  2531. SinCosine(Phi, sinPhi, cosPhi);
  2532. dist := fArcRadius + fBottomRadius * cosPhi;
  2533. FMesh[MeshIndex][j].Position := Vector3fMake(cosTheta1 * dist,
  2534. -sinTheta1 * dist, FBottomRadius * sinPhi);
  2535. ringDir := FMesh[MeshIndex][j].Position;
  2536. ringDir.Z := 0.0;
  2537. NormalizeVector(ringDir);
  2538. FMesh[MeshIndex][j].Normal := VectorCrossProduct(ZVector, ringDir);
  2539. FMesh[MeshIndex][j].Tangent := ringDir;
  2540. FMesh[MeshIndex][j].Binormal := ZVector;
  2541. FMesh[MeshIndex][j].TexCoord := Vector2fMake(0, j * jFact);
  2542. end;
  2543. ConeCenter.Position := Vector3fMake(cosTheta1 * fArcRadius,
  2544. -sinTheta1 * fArcRadius, 0);
  2545. ConeCenter.Normal := FMesh[MeshIndex][0].Normal;
  2546. ConeCenter.Tangent := FMesh[MeshIndex][0].Tangent;
  2547. ConeCenter.Binormal := FMesh[MeshIndex][0].Binormal;
  2548. ConeCenter.TexCoord := Vector2fMake(1, 1);
  2549. begin
  2550. if GL.ARB_shader_objects and (rci.GLStates.CurrentProgram > 0) then
  2551. begin
  2552. TanLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2553. PAnsiChar(TangentAttributeName));
  2554. BinLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2555. PAnsiChar(BinormalAttributeName));
  2556. end
  2557. else
  2558. begin
  2559. TanLoc := -1;
  2560. BinLoc := TanLoc;
  2561. end;
  2562. gl.Begin_(GL_TRIANGLE_FAN);
  2563. pVertex := @ConeCenter;
  2564. EmitVertex(pVertex, TanLoc, BinLoc);
  2565. for j := 0 to FSlices do
  2566. begin
  2567. pVertex := @FMesh[MeshIndex][j];
  2568. EmitVertex(pVertex, TanLoc, BinLoc);
  2569. end;
  2570. gl.End_;
  2571. end;
  2572. MeshIndex := MeshIndex + 1;
  2573. end;
  2574. if aaTopArrow in FParts then
  2575. begin
  2576. SetLength(FMesh[MeshIndex], FSlices + 1);
  2577. SetLength(FMesh[MeshIndex + 1], FSlices + 1);
  2578. SetLength(FMesh[MeshIndex + 2], FSlices + 1);
  2579. Theta1 := DegToRadian(FStopAngle - StopOffset);
  2580. SinCosine(Theta1, sinTheta1, cosTheta1);
  2581. ConeCenter.Position := Vector3fMake(cosTheta1 * fArcRadius,
  2582. -sinTheta1 * fArcRadius, 0);
  2583. Phi := 0;
  2584. for j := FSlices downto 0 do
  2585. begin
  2586. Phi := Phi + sideDelta;
  2587. SinCosine(Phi, sinPhi, cosPhi);
  2588. dist := fArcRadius + fTopArrowHeadRadius * cosPhi;
  2589. // Cap
  2590. FMesh[MeshIndex][j].Position := Vector3fMake(cosTheta1 * dist,
  2591. -sinTheta1 * dist, fTopArrowHeadRadius * sinPhi);
  2592. ringDir := FMesh[MeshIndex][j].Position;
  2593. ringDir.Z := 0.0;
  2594. NormalizeVector(ringDir);
  2595. FMesh[MeshIndex][j].Normal := VectorCrossProduct(ZVector, ringDir);
  2596. FMesh[MeshIndex][j].Tangent := ringDir;
  2597. FMesh[MeshIndex][j].Binormal := ZVector;
  2598. FMesh[MeshIndex][j].TexCoord := Vector2fMake(0, j * jFact);
  2599. // Cone
  2600. FMesh[MeshIndex + 1][j].Position := Vector3fMake(cosTheta1 * dist,
  2601. -sinTheta1 * dist, fTopArrowHeadRadius * sinPhi);
  2602. FMesh[MeshIndex + 2][j].Position := VectorSubtract(ConeCenter.Position,
  2603. Vector3fMake(sinTheta1 * fTopArrowHeadHeight,
  2604. cosTheta1 * fTopArrowHeadHeight, 0));
  2605. FMesh[MeshIndex + 1][j].Tangent :=
  2606. VectorNormalize(VectorSubtract(FMesh[MeshIndex + 2][j].Position,
  2607. FMesh[MeshIndex + 1][j].Position));
  2608. FMesh[MeshIndex + 2][j].Tangent := FMesh[MeshIndex + 1][j].Tangent;
  2609. FMesh[MeshIndex + 1][j].Binormal := Vector3fMake(cosTheta1 * -sinPhi,
  2610. sinTheta1 * sinPhi, cosPhi);
  2611. FMesh[MeshIndex + 2][j].Binormal := FMesh[MeshIndex + 1][j].Binormal;
  2612. FMesh[MeshIndex + 1][j].Normal :=
  2613. VectorCrossProduct(FMesh[MeshIndex + 1][j].Binormal,
  2614. FMesh[MeshIndex + 1][j].Tangent);
  2615. FMesh[MeshIndex + 2][j].Normal := FMesh[MeshIndex + 1][j].Normal;
  2616. FMesh[MeshIndex + 1][j].TexCoord := Vector2fMake(1, j * jFact);
  2617. FMesh[MeshIndex + 2][j].TexCoord := Vector2fMake(0, j * jFact);
  2618. end;
  2619. ConeCenter.Normal := FMesh[MeshIndex][0].Normal;
  2620. ConeCenter.Tangent := FMesh[MeshIndex][0].Tangent;
  2621. ConeCenter.Binormal := FMesh[MeshIndex][0].Binormal;
  2622. ConeCenter.TexCoord := Vector2fMake(1, 1);
  2623. begin
  2624. if GL.ARB_shader_objects and (rci.GLStates.CurrentProgram > 0) then
  2625. begin
  2626. TanLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2627. PAnsiChar(TangentAttributeName));
  2628. BinLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2629. PAnsiChar(BinormalAttributeName));
  2630. end
  2631. else
  2632. begin
  2633. TanLoc := -1;
  2634. BinLoc := TanLoc;
  2635. end;
  2636. gl.Begin_(GL_TRIANGLE_FAN);
  2637. pVertex := @ConeCenter;
  2638. EmitVertex(pVertex, TanLoc, BinLoc);
  2639. for j := 0 to FSlices do
  2640. begin
  2641. pVertex := @FMesh[MeshIndex][j];
  2642. EmitVertex(pVertex, TanLoc, BinLoc);
  2643. end;
  2644. gl.End_;
  2645. gl.Begin_(GL_TRIANGLES);
  2646. for j := FSlices - 1 downto 0 do
  2647. begin
  2648. pVertex := @FMesh[MeshIndex + 2][j];
  2649. EmitVertex(pVertex, TanLoc, BinLoc);
  2650. pVertex := @FMesh[MeshIndex + 2][j + 1];
  2651. EmitVertex(pVertex, TanLoc, BinLoc);
  2652. pVertex := @FMesh[MeshIndex + 1][j];
  2653. EmitVertex(pVertex, TanLoc, BinLoc);
  2654. pVertex := @FMesh[MeshIndex + 1][j + 1];
  2655. EmitVertex(pVertex, TanLoc, BinLoc);
  2656. pVertex := @FMesh[MeshIndex + 1][j];
  2657. EmitVertex(pVertex, TanLoc, BinLoc);
  2658. pVertex := @FMesh[MeshIndex + 2][j + 1];
  2659. EmitVertex(pVertex, TanLoc, BinLoc);
  2660. end;
  2661. gl.End_;
  2662. end;
  2663. end
  2664. else
  2665. begin
  2666. SetLength(FMesh[MeshIndex], FSlices + 1);
  2667. Theta1 := DegToRadian(FStopAngle);
  2668. SinCosine(Theta1, sinTheta1, cosTheta1);
  2669. Phi := 0;
  2670. for j := FSlices downto 0 do
  2671. begin
  2672. Phi := Phi + sideDelta;
  2673. SinCosine(Phi, sinPhi, cosPhi);
  2674. dist := fArcRadius + fTopRadius * cosPhi;
  2675. FMesh[MeshIndex][j].Position := Vector3fMake(cosTheta1 * dist,
  2676. -sinTheta1 * dist, fTopRadius * sinPhi);
  2677. ringDir := FMesh[MeshIndex][j].Position;
  2678. ringDir.Z := 0.0;
  2679. NormalizeVector(ringDir);
  2680. FMesh[MeshIndex][j].Normal := VectorCrossProduct(ringDir, ZVector);
  2681. FMesh[MeshIndex][j].Tangent := ringDir;
  2682. FMesh[MeshIndex][j].Binormal := VectorNegate(ZVector);
  2683. FMesh[MeshIndex][j].TexCoord := Vector2fMake(1, j * jFact);
  2684. end;
  2685. ConeCenter.Position := Vector3fMake(cosTheta1 * fArcRadius,
  2686. -sinTheta1 * fArcRadius, 0);
  2687. ConeCenter.Normal := FMesh[MeshIndex][0].Normal;
  2688. ConeCenter.Tangent := FMesh[MeshIndex][0].Tangent;
  2689. ConeCenter.Binormal := FMesh[MeshIndex][0].Binormal;
  2690. ConeCenter.TexCoord := Vector2fMake(0, 0);
  2691. begin
  2692. if GL.ARB_shader_objects and (rci.GLStates.CurrentProgram > 0) then
  2693. begin
  2694. TanLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2695. PAnsiChar(TangentAttributeName));
  2696. BinLoc := gl.GetAttribLocation(rci.GLStates.CurrentProgram,
  2697. PAnsiChar(BinormalAttributeName));
  2698. end
  2699. else
  2700. begin
  2701. TanLoc := -1;
  2702. BinLoc := TanLoc;
  2703. end;
  2704. gl.Begin_(GL_TRIANGLE_FAN);
  2705. pVertex := @ConeCenter;
  2706. EmitVertex(pVertex, TanLoc, BinLoc);
  2707. for j := FSlices downto 0 do
  2708. begin
  2709. pVertex := @FMesh[MeshIndex][j];
  2710. EmitVertex(pVertex, TanLoc, BinLoc);
  2711. end;
  2712. gl.End_;
  2713. end;
  2714. end;
  2715. end;
  2716. end;
  2717. procedure TGLArrowArc.Assign(Source: TPersistent);
  2718. begin
  2719. if Assigned(Source) and (Source is TGLArrowLine) then
  2720. begin
  2721. FStartAngle := TGLArrowArc(Source).FStartAngle;
  2722. FStopAngle := TGLArrowArc(Source).FStopAngle;
  2723. fArcRadius := TGLArrowArc(Source).fArcRadius;
  2724. FParts := TGLArrowArc(Source).FParts;
  2725. FTopRadius := TGLArrowArc(Source).FTopRadius;
  2726. fTopArrowHeadHeight := TGLArrowArc(Source).fTopArrowHeadHeight;
  2727. fTopArrowHeadRadius := TGLArrowArc(Source).fTopArrowHeadRadius;
  2728. fBottomArrowHeadHeight := TGLArrowArc(Source).fBottomArrowHeadHeight;
  2729. fBottomArrowHeadRadius := TGLArrowArc(Source).fBottomArrowHeadRadius;
  2730. FHeadStackingStyle := TGLArrowArc(Source).FHeadStackingStyle;
  2731. end;
  2732. inherited Assign(Source);
  2733. end;
  2734. // ------------------
  2735. // ------------------ TGLFrustrum ------------------
  2736. // ------------------
  2737. constructor TGLFrustrum.Create(AOwner: TComponent);
  2738. begin
  2739. inherited Create(AOwner);
  2740. FApexHeight := 1;
  2741. FBaseWidth := 1;
  2742. FBaseDepth := 1;
  2743. FHeight := 0.5;
  2744. FParts := cAllFrustrumParts;
  2745. FNormalDirection := ndOutside;
  2746. end;
  2747. procedure TGLFrustrum.BuildList(var rci: TGLRenderContextInfo);
  2748. var
  2749. HBW, HBD: Single; // half of width, half of depth at base
  2750. HTW, HTD: Single; // half of width, half of depth at top of frustrum
  2751. HFH: Single; // half of height, for align to center
  2752. Sign: Single; // +1 or -1
  2753. angle: Single; // in radians
  2754. ASin, ACos: Single;
  2755. begin
  2756. if FNormalDirection = ndInside then
  2757. Sign := -1
  2758. else
  2759. Sign := 1;
  2760. HBW := FBaseWidth * 0.5;
  2761. HBD := FBaseDepth * 0.5;
  2762. HTW := HBW * (FApexHeight - FHeight) / FApexHeight;
  2763. HTD := HBD * (FApexHeight - FHeight) / FApexHeight;
  2764. HFH := FHeight * 0.5;
  2765. gl.Begin_(GL_QUADS);
  2766. if [fpFront, fpBack] * FParts <> [] then
  2767. begin
  2768. angle := ArcTan(FApexHeight / HBD);
  2769. // angle of front plane with bottom plane
  2770. SinCosine(angle, ASin, ACos);
  2771. if fpFront in FParts then
  2772. begin
  2773. gl.Normal3f(0, Sign * ACos, Sign * ASin);
  2774. XGL.TexCoord2fv(@XYTexPoint);
  2775. gl.Vertex3f(HTW, HFH, HTD);
  2776. XGL.TexCoord2fv(@YTexPoint);
  2777. gl.Vertex3f(-HTW, HFH, HTD);
  2778. XGL.TexCoord2fv(@NullTexPoint);
  2779. gl.Vertex3f(-HBW, -HFH, HBD);
  2780. XGL.TexCoord2fv(@XTexPoint);
  2781. gl.Vertex3f(HBW, -HFH, HBD);
  2782. end;
  2783. if fpBack in FParts then
  2784. begin
  2785. gl.Normal3f(0, Sign * ACos, -Sign * ASin);
  2786. XGL.TexCoord2fv(@YTexPoint);
  2787. gl.Vertex3f(HTW, HFH, -HTD);
  2788. XGL.TexCoord2fv(@NullTexPoint);
  2789. gl.Vertex3f(HBW, -HFH, -HBD);
  2790. XGL.TexCoord2fv(@XTexPoint);
  2791. gl.Vertex3f(-HBW, -HFH, -HBD);
  2792. XGL.TexCoord2fv(@XYTexPoint);
  2793. gl.Vertex3f(-HTW, HFH, -HTD);
  2794. end;
  2795. end;
  2796. if [fpLeft, fpRight] * FParts <> [] then
  2797. begin
  2798. angle := ArcTan(FApexHeight / HBW); // angle of side plane with bottom plane
  2799. SinCosine(angle, ASin, ACos);
  2800. if fpLeft in FParts then
  2801. begin
  2802. gl.Normal3f(-Sign * ASin, Sign * ACos, 0);
  2803. XGL.TexCoord2fv(@XYTexPoint);
  2804. gl.Vertex3f(-HTW, HFH, HTD);
  2805. XGL.TexCoord2fv(@YTexPoint);
  2806. gl.Vertex3f(-HTW, HFH, -HTD);
  2807. XGL.TexCoord2fv(@NullTexPoint);
  2808. gl.Vertex3f(-HBW, -HFH, -HBD);
  2809. XGL.TexCoord2fv(@XTexPoint);
  2810. gl.Vertex3f(-HBW, -HFH, HBD);
  2811. end;
  2812. if fpRight in FParts then
  2813. begin
  2814. gl.Normal3f(Sign * ASin, Sign * ACos, 0);
  2815. XGL.TexCoord2fv(@YTexPoint);
  2816. gl.Vertex3f(HTW, HFH, HTD);
  2817. XGL.TexCoord2fv(@NullTexPoint);
  2818. gl.Vertex3f(HBW, -HFH, HBD);
  2819. XGL.TexCoord2fv(@XTexPoint);
  2820. gl.Vertex3f(HBW, -HFH, -HBD);
  2821. XGL.TexCoord2fv(@XYTexPoint);
  2822. gl.Vertex3f(HTW, HFH, -HTD);
  2823. end;
  2824. end;
  2825. if (fpTop in FParts) and (FHeight < FApexHeight) then
  2826. begin
  2827. gl.Normal3f(0, Sign, 0);
  2828. XGL.TexCoord2fv(@YTexPoint);
  2829. gl.Vertex3f(-HTW, HFH, -HTD);
  2830. XGL.TexCoord2fv(@NullTexPoint);
  2831. gl.Vertex3f(-HTW, HFH, HTD);
  2832. XGL.TexCoord2fv(@XTexPoint);
  2833. gl.Vertex3f(HTW, HFH, HTD);
  2834. XGL.TexCoord2fv(@XYTexPoint);
  2835. gl.Vertex3f(HTW, HFH, -HTD);
  2836. end;
  2837. if fpBottom in FParts then
  2838. begin
  2839. gl.Normal3f(0, -Sign, 0);
  2840. XGL.TexCoord2fv(@NullTexPoint);
  2841. gl.Vertex3f(-HBW, -HFH, -HBD);
  2842. XGL.TexCoord2fv(@XTexPoint);
  2843. gl.Vertex3f(HBW, -HFH, -HBD);
  2844. XGL.TexCoord2fv(@XYTexPoint);
  2845. gl.Vertex3f(HBW, -HFH, HBD);
  2846. XGL.TexCoord2fv(@YTexPoint);
  2847. gl.Vertex3f(-HBW, -HFH, HBD);
  2848. end;
  2849. gl.End_;
  2850. end;
  2851. procedure TGLFrustrum.SetApexHeight(const aValue: Single);
  2852. begin
  2853. if (aValue <> FApexHeight) and (aValue >= 0) then
  2854. begin
  2855. FApexHeight := aValue;
  2856. if FHeight > aValue then
  2857. FHeight := aValue;
  2858. StructureChanged;
  2859. end;
  2860. end;
  2861. procedure TGLFrustrum.SetBaseDepth(const aValue: Single);
  2862. begin
  2863. if (aValue <> FBaseDepth) and (aValue >= 0) then
  2864. begin
  2865. FBaseDepth := aValue;
  2866. StructureChanged;
  2867. end;
  2868. end;
  2869. procedure TGLFrustrum.SetBaseWidth(const aValue: Single);
  2870. begin
  2871. if (aValue <> FBaseWidth) and (aValue >= 0) then
  2872. begin
  2873. FBaseWidth := aValue;
  2874. StructureChanged;
  2875. end;
  2876. end;
  2877. procedure TGLFrustrum.SetHeight(const aValue: Single);
  2878. begin
  2879. if (aValue <> FHeight) and (aValue >= 0) then
  2880. begin
  2881. FHeight := aValue;
  2882. if FApexHeight < aValue then
  2883. FApexHeight := aValue;
  2884. StructureChanged;
  2885. end;
  2886. end;
  2887. procedure TGLFrustrum.SetParts(aValue: TFrustrumParts);
  2888. begin
  2889. if aValue <> FParts then
  2890. begin
  2891. FParts := aValue;
  2892. StructureChanged;
  2893. end;
  2894. end;
  2895. procedure TGLFrustrum.SetNormalDirection(aValue: TGLNormalDirection);
  2896. begin
  2897. if aValue <> FNormalDirection then
  2898. begin
  2899. FNormalDirection := aValue;
  2900. StructureChanged;
  2901. end;
  2902. end;
  2903. procedure TGLFrustrum.Assign(Source: TPersistent);
  2904. begin
  2905. if Assigned(Source) and (Source is TGLFrustrum) then
  2906. begin
  2907. FApexHeight := TGLFrustrum(Source).FApexHeight;
  2908. FBaseDepth := TGLFrustrum(Source).FBaseDepth;
  2909. FBaseWidth := TGLFrustrum(Source).FBaseWidth;
  2910. FHeight := TGLFrustrum(Source).FHeight;
  2911. FParts := TGLFrustrum(Source).FParts;
  2912. FNormalDirection := TGLFrustrum(Source).FNormalDirection;
  2913. end;
  2914. inherited Assign(Source);
  2915. end;
  2916. function TGLFrustrum.TopDepth: Single;
  2917. begin
  2918. Result := FBaseDepth * (FApexHeight - FHeight) / FApexHeight;
  2919. end;
  2920. function TGLFrustrum.TopWidth: Single;
  2921. begin
  2922. Result := FBaseWidth * (FApexHeight - FHeight) / FApexHeight;
  2923. end;
  2924. procedure TGLFrustrum.DefineProperties(Filer: TFiler);
  2925. begin
  2926. inherited;
  2927. Filer.DefineBinaryProperty('FrustrumSize', ReadData, WriteData,
  2928. (FApexHeight <> 1) or (FBaseDepth <> 1) or (FBaseWidth <> 1) or
  2929. (FHeight <> 0.5));
  2930. end;
  2931. procedure TGLFrustrum.ReadData(Stream: TStream);
  2932. begin
  2933. with Stream do
  2934. begin
  2935. Read(FApexHeight, SizeOf(FApexHeight));
  2936. Read(FBaseDepth, SizeOf(FBaseDepth));
  2937. Read(FBaseWidth, SizeOf(FBaseWidth));
  2938. Read(FHeight, SizeOf(FHeight));
  2939. end;
  2940. end;
  2941. procedure TGLFrustrum.WriteData(Stream: TStream);
  2942. begin
  2943. with Stream do
  2944. begin
  2945. Write(FApexHeight, SizeOf(FApexHeight));
  2946. Write(FBaseDepth, SizeOf(FBaseDepth));
  2947. Write(FBaseWidth, SizeOf(FBaseWidth));
  2948. Write(FHeight, SizeOf(FHeight));
  2949. end;
  2950. end;
  2951. function TGLFrustrum.AxisAlignedBoundingBoxUnscaled: TAABB;
  2952. var
  2953. aabb: TAABB;
  2954. child: TGLBaseSceneObject;
  2955. i: integer;
  2956. begin
  2957. SetAABB(Result, AxisAlignedDimensionsUnscaled);
  2958. OffsetAABB(Result, VectorMake(0, FHeight * 0.5, 0));
  2959. // not tested for child objects
  2960. for i := 0 to Count - 1 do
  2961. begin
  2962. child := TGLBaseSceneObject(Children[i]);
  2963. aabb := child.AxisAlignedBoundingBoxUnscaled;
  2964. AABBTransform(aabb, child.Matrix^);
  2965. AddAABB(Result, aabb);
  2966. end;
  2967. end;
  2968. function TGLFrustrum.AxisAlignedDimensionsUnscaled: TVector;
  2969. begin
  2970. Result.X := FBaseWidth * 0.5;
  2971. Result.Y := FHeight * 0.5;
  2972. Result.Z := FBaseDepth * 0.5;
  2973. Result.W := 0;
  2974. end;
  2975. // ------------------
  2976. // ------------------ TGLPolygon ------------------
  2977. // ------------------
  2978. constructor TGLPolygon.Create(AOwner: TComponent);
  2979. begin
  2980. inherited Create(AOwner);
  2981. FParts := [ppTop, ppBottom];
  2982. end;
  2983. destructor TGLPolygon.Destroy;
  2984. begin
  2985. inherited Destroy;
  2986. end;
  2987. procedure TGLPolygon.SetParts(const val: TGLPolygonParts);
  2988. begin
  2989. if FParts <> val then
  2990. begin
  2991. FParts := val;
  2992. StructureChanged;
  2993. end;
  2994. end;
  2995. procedure TGLPolygon.Assign(Source: TPersistent);
  2996. begin
  2997. if Source is TGLPolygon then
  2998. begin
  2999. FParts := TGLPolygon(Source).FParts;
  3000. end;
  3001. inherited Assign(Source);
  3002. end;
  3003. procedure TGLPolygon.BuildList(var rci: TGLRenderContextInfo);
  3004. var
  3005. Normal: TAffineVector;
  3006. pNorm: PAffineVector;
  3007. begin
  3008. if (Nodes.Count > 1) then
  3009. begin
  3010. Normal := Nodes.Normal;
  3011. if VectorIsNull(Normal) then
  3012. pNorm := nil
  3013. else
  3014. pNorm := @Normal;
  3015. if ppTop in FParts then
  3016. begin
  3017. if SplineMode = lsmLines then
  3018. Nodes.RenderTesselatedPolygon(true, pNorm, 1)
  3019. else
  3020. Nodes.RenderTesselatedPolygon(true, pNorm, Division);
  3021. end;
  3022. // tessellate bottom polygon
  3023. if ppBottom in FParts then
  3024. begin
  3025. if Assigned(pNorm) then
  3026. NegateVector(Normal);
  3027. if SplineMode = lsmLines then
  3028. Nodes.RenderTesselatedPolygon(true, pNorm, 1, true)
  3029. else
  3030. Nodes.RenderTesselatedPolygon(true, pNorm, Division, true);
  3031. end;
  3032. end;
  3033. end;
  3034. // -------------------------------------------------------------
  3035. initialization
  3036. // -------------------------------------------------------------
  3037. RegisterClasses([TGLCylinder, TGLCone, TGLTorus, TGLDisk, TGLArrowLine,
  3038. TGLAnnulus, TGLFrustrum, TGLPolygon, TGLCapsule, TGLArrowArc]);
  3039. end.