GXS.GeomObjects.pas 111 KB

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