GLGeomObjects.pas 99 KB

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