GLS.GeomObjects.pas 111 KB

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