123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764 |
- //
- // The graphics engine GLScene
- //
- unit GLS.Extrusion;
- (*
- Extrusion objects are solids defined by the surface described by a moving curve.
- Suggestion:
- All extrusion objects use actually the same kind of "parts",
- one common type should do.
- The registered class is:
- [TGLRevolutionSolid, TGLExtrusionSolid, TGLPipe]
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- System.Classes,
- System.SysUtils,
- System.Math,
- Stage.OpenGLTokens,
- GLS.Context,
- GLS.Objects,
- GLS.Scene,
- GLS.MultiPolygon,
- GLS.Color,
- Stage.VectorGeometry,
- GLS.RenderContextInfo,
- GLS.Nodes,
- GLS.State,
- Stage.VectorTypes;
- type
- TGLExtrusionSolidPart = (espOutside, espInside, espStartPolygon, espStopPolygon);
- TGLExtrusionSolidParts = set of TGLExtrusionSolidPart;
- TGLRevolutionSolidPart = (rspOutside, rspInside, rspStartPolygon, rspStopPolygon);
- TGLRevolutionSolidParts = set of TGLRevolutionSolidPart;
- (* A solid object generated by rotating a curve along the Y axis.
- The curve is described by the Nodes and SplineMode properties, and it is
- rotated in the trigonometrical direction (CCW when seen from Y->INF).
- The TGLRevolutionSolid can also be used to render regular helicoidions, by
- setting a non-null YOffsetPerTurn, and adjusting start/finish angles to
- make more than one revolution.
- If you want top/bottom caps, just add a first/last node that will make
- the curve start/finish on the Y axis. *)
- TGLRevolutionSolid = class(TGLPolygonBase)
- private
- FSlices: Integer;
- FStartAngle, FStopAngle: Single;
- FNormals: TGLNormalSmoothing;
- FYOffsetPerTurn: Single;
- FTriangleCount: Integer;
- FNormalDirection: TGLNormalDirection;
- FParts: TGLRevolutionSolidParts;
- FAxisAlignedDimensionsCache: TGLVector;
- protected
- procedure SetStartAngle(const val: Single);
- procedure SetStopAngle(const val: Single);
- function StoreStopAngle: Boolean;
- procedure SetSlices(const val: Integer);
- procedure SetNormals(const val: TGLNormalSmoothing);
- procedure SetYOffsetPerTurn(const val: Single);
- procedure SetNormalDirection(const val: TGLNormalDirection);
- procedure SetParts(const val: TGLRevolutionSolidParts);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var rci: TGLRenderContextInfo); override;
- // Number of triangles used for rendering.
- property TriangleCount: Integer read FTriangleCount;
- function AxisAlignedDimensionsUnscaled: TGLVector; override;
- procedure StructureChanged; override;
- published
- (* Parts of the rotation solid to be generated for rendering.
- rspInside and rspOutside are generated from the curve and make the
- inside/outside as long as NormalDirection=ndOutside and the solid
- is described by the curve that goes from top to bottom.
- Start/StopPolygon are tesselated from the curve (considered as closed). *)
- property Parts: TGLRevolutionSolidParts read FParts write SetParts default [rspOutside];
- property StartAngle: Single read FStartAngle write SetStartAngle;
- property StopAngle: Single read FStopAngle write SetStopAngle stored StoreStopAngle;
- (* Y offset applied to the curve position for each turn.
- This amount is applied proportionnally, for instance if your curve
- is a small circle, off from the Y axis, with a YOffset set to 0 (zero),
- you will get a torus, but with a non null value, you will get a
- small helicoidal spring.
- This can be useful for rendering, lots of helicoidal objects from
- screws, to nails to stairs etc. *)
- property YOffsetPerTurn: Single read FYOffsetPerTurn write
- SetYOffsetPerTurn;
- // Number of slices per turn (360deg).
- property Slices: Integer read FSlices write SetSlices default 16;
- property Normals: TGLNormalSmoothing read FNormals write SetNormals default
- nsFlat;
- property NormalDirection: TGLNormalDirection read FNormalDirection write
- SetNormalDirection default ndOutside;
- end;
- (* Extrudes a complex Polygon into Z direction.
- For contour description see TGLMultiPolygonBase.
- properties Parts, Height (or should we better cal it Depth, because its in Z?),
- Stacks, Normals and NormalDirection are equivalent to TGLRevolutionSolid.
- If Normals=nsSmooth and the angle between two consecutive normals along the
- contour is less than MinSmoothAngle, smoothing is done, otherweise flat normals
- are used. This makes it possible to have smooth normals on sharp edged contours. *)
- TGLExtrusionSolid = class(TGLMultiPolygonBase)
- private
- FStacks: Integer;
- FNormals: TGLNormalSmoothing;
- FTriangleCount: Integer;
- FNormalDirection: TGLNormalDirection;
- FParts: TGLExtrusionSolidParts;
- FHeight: TGLFloat;
- FMinSmoothAngle: Single;
- FMinSmoothAngleCos: Single;
- FAxisAlignedDimensionsCache: TGLVector;
- procedure SetHeight(const Value: TGLFloat);
- procedure SetMinSmoothAngle(const Value: Single);
- protected
- procedure SetStacks(const val: Integer);
- procedure SetNormals(const val: TGLNormalSmoothing);
- procedure SetNormalDirection(const val: TGLNormalDirection);
- procedure SetParts(const val: TGLExtrusionSolidParts);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var rci: TGLRenderContextInfo); override;
- // Number of triangles used for rendering.
- property TriangleCount: Integer read FTriangleCount;
- function AxisAlignedDimensionsUnscaled: TGLVector; override;
- procedure StructureChanged; override;
- published
- property Parts: TGLExtrusionSolidParts read FParts write SetParts default [espOutside];
- property Height: TGLFloat read FHeight write SetHeight;
- property Stacks: Integer read FStacks write SetStacks default 1;
- property Normals: TGLNormalSmoothing read FNormals write SetNormals default nsFlat;
- property NormalDirection: TGLNormalDirection read FNormalDirection write SetNormalDirection default ndOutside;
- property MinSmoothAngle: Single read FMinSmoothAngle write SetMinSmoothAngle;
- end;
- TGLPipeNode = class(TGLNode)
- private
- FRadiusFactor: Single;
- FColor: TGLColor;
- FTexCoordT: Single;
- protected
- function GetDisplayName: string; override;
- procedure SetRadiusFactor(const val: Single);
- function StoreRadiusFactor: Boolean;
- procedure SetColor(const val: TGLColor);
- procedure ColorChanged(sender: TObject);
- function StoreTexCoordT: Boolean;
- public
- constructor Create(Collection: TCollection); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- published
- property RadiusFactor: Single read FRadiusFactor write SetRadiusFactor stored
- StoreRadiusFactor;
- property Color: TGLColor read FColor write SetColor;
- property TexCoordT: Single read FTexCoordT write FTexCoordT stored
- StoreTexCoordT;
- end;
- TGLPipeNodes = class(TGLLinesNodes)
- protected
- procedure SetItems(index: Integer; const val: TGLPipeNode);
- function GetItems(index: Integer): TGLPipeNode;
- public
- constructor Create(AOwner: TComponent);
- function Add: TGLPipeNode;
- function FindItemID(ID: Integer): TGLPipeNode;
- property Items[index: Integer]: TGLPipeNode read GetItems write SetItems; default;
- end;
- TPipePart = (ppOutside, ppInside, ppStartDisk, ppStopDisk);
- TPipeParts = set of TPipePart;
- TPipeNodesColorMode = (pncmNone, pncmEmission, pncmAmbient, pncmDiffuse,
- pncmAmbientAndDiffuse);
- TPipeTexCoordMode = (ptcmDefault, ptcmManual);
- TPipeNormalMode = (pnmDefault, pnmAdvanced);
- (* A solid object generated by extruding a circle along a trajectory.
- Texture coordinates NOT supported yet. *)
- TGLPipe = class(TGLPolygonBase)
- private
- FSlices: Integer;
- FParts: TPipeParts;
- FTriangleCount: Integer;
- FRadius: Single;
- FNodesColorMode: TPipeNodesColorMode;
- FTextCoordMode: TPipeTexCoordMode;
- FTextCoordTileS: Single;
- FTextCoordTileT: Single;
- FNormalMode: TPipeNormalMode;
- FNormalSmoothAngle: Single;
- protected
- procedure CreateNodes; override;
- procedure SetSlices(const val: Integer);
- procedure SetParts(const val: TPipeParts);
- procedure SetRadius(const val: Single);
- function StoreRadius: Boolean;
- procedure SetNodesColorMode(const val: TPipeNodesColorMode);
- procedure SetTextCoordMode(const val: TPipeTexCoordMode);
- procedure SetTextCoordTileS(const val: Single);
- procedure SetTextCoordTileT(const val: Single);
- function StoreTextCoordTileS: Boolean;
- function StoreTextCoordTileT: Boolean;
- procedure SetNormalMode(const val: TPipeNormalMode);
- procedure SetNormalSmoothAngle(const val: Single);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var rci: TGLRenderContextInfo); override;
- // Number of triangles used for rendering.
- property TriangleCount: Integer read FTriangleCount;
- published
- property Parts: TPipeParts read FParts write SetParts default [ppOutside];
- property Slices: Integer read FSlices write SetSlices default 16;
- property Radius: Single read FRadius write SetRadius;
- property NodesColorMode: TPipeNodesColorMode read FNodesColorMode write
- SetNodesColorMode default pncmNone;
- property TexCoordMode: TPipeTexCoordMode read FTextCoordMode
- write SetTextCoordMode default ptcmDefault;
- property TexCoordTileS: Single read FTextCoordTileS write SetTextCoordTileS
- stored StoreTextCoordTileS;
- property TexCoordTileT: Single read FTextCoordTileT write SetTextCoordTileT
- stored StoreTextCoordTileT;
- property NormalMode: TPipeNormalMode read FNormalMode write SetNormalMode
- default pnmDefault;
- property NormalSmoothAngle: Single read FNormalSmoothAngle write
- SetNormalSmoothAngle;
- end;
- // ------------------------------------------------------------------
- implementation
- // ------------------------------------------------------------------
- uses
- Stage.Spline,
- GLS.VectorLists,
- GLS.XOpenGL;
- // ------------------
- // ------------------ TGLRevolutionSolid ------------------
- // ------------------
- constructor TGLRevolutionSolid.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FStartAngle := 0;
- FStopAngle := 360;
- FSlices := 16;
- FNormals := nsFlat;
- FNormalDirection := ndOutside;
- FParts := [rspOutside];
- end;
- destructor TGLRevolutionSolid.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TGLRevolutionSolid.SetStartAngle(const val: Single);
- begin
- if FStartAngle <> val then
- begin
- FStartAngle := val;
- if FStartAngle > FStopAngle then
- FStopAngle := FStartAngle;
- StructureChanged;
- end;
- end;
- procedure TGLRevolutionSolid.SetStopAngle(const val: Single);
- begin
- if FStopAngle <> val then
- begin
- FStopAngle := val;
- if FStopAngle < FStartAngle then
- FStartAngle := FStopAngle;
- StructureChanged;
- end;
- end;
- function TGLRevolutionSolid.StoreStopAngle: Boolean;
- begin
- Result := (FStopAngle <> 360);
- end;
- procedure TGLRevolutionSolid.SetSlices(const val: Integer);
- begin
- if (val <> FSlices) and (val > 0) then
- begin
- FSlices := val;
- StructureChanged;
- end;
- end;
- procedure TGLRevolutionSolid.SetNormals(const val: TGLNormalSmoothing);
- begin
- if FNormals <> val then
- begin
- FNormals := val;
- StructureChanged;
- end;
- end;
- procedure TGLRevolutionSolid.SetYOffsetPerTurn(const val: Single);
- begin
- if FYOffsetPerTurn <> val then
- begin
- FYOffsetPerTurn := val;
- StructureChanged;
- end;
- end;
- procedure TGLRevolutionSolid.SetNormalDirection(const val: TGLNormalDirection);
- begin
- if FNormalDirection <> val then
- begin
- FNormalDirection := val;
- StructureChanged;
- end;
- end;
- procedure TGLRevolutionSolid.SetParts(const val: TGLRevolutionSolidParts);
- begin
- if FParts <> val then
- begin
- FParts := val;
- StructureChanged;
- end;
- end;
- procedure TGLRevolutionSolid.Assign(Source: TPersistent);
- begin
- if Source is TGLRevolutionSolid then
- begin
- FStartAngle := TGLRevolutionSolid(Source).FStartAngle;
- FStopAngle := TGLRevolutionSolid(Source).FStopAngle;
- FSlices := TGLRevolutionSolid(Source).FSlices;
- FNormals := TGLRevolutionSolid(Source).FNormals;
- FYOffsetPerTurn := TGLRevolutionSolid(Source).FYOffsetPerTurn;
- FNormalDirection := TGLRevolutionSolid(Source).FNormalDirection;
- FParts := TGLRevolutionSolid(Source).FParts;
- end;
- inherited Assign(Source);
- end;
- procedure TGLRevolutionSolid.BuildList(var rci: TGLRenderContextInfo);
- var
- deltaAlpha, startAlpha, stopAlpha, alpha: Single;
- deltaS: Single;
- deltaYOffset, yOffset, startYOffset: Single;
- lastNormals: PAffineVectorArray;
- firstStep, gotYDeltaOffset: Boolean;
- procedure CalcNormal(const ptTop, ptBottom: PAffineVector; var normal:
- TAffineVector);
- var
- tb: TAffineVector;
- mx, mz: Single;
- begin
- mx := ptBottom^.X + ptTop^.X;
- mz := ptBottom^.Z + ptTop^.Z;
- VectorSubtract(ptBottom^, ptTop^, tb);
- normal.X := -tb.Y * mx;
- normal.Y := mx * tb.X + mz * tb.Z;
- normal.Z := -mz * tb.Y;
- NormalizeVector(normal);
- end;
- procedure BuildStep(ptTop, ptBottom: PAffineVector; invertNormals: Boolean;
- topT, bottomT: Single);
- var
- i: Integer;
- topBase, topNext, bottomBase, bottomNext, normal, topNormal, bottomNormal:
- TAffineVector;
- topTPBase, topTPNext, bottomTPBase, bottomTPNext: TTexPoint;
- nextAlpha: Single;
- ptBuffer: PAffineVector;
- procedure SetLocalNormals;
- begin
- if (FNormals = nsFlat) or FirstStep then
- begin
- topNormal := normal;
- bottomNormal := normal;
- if (FNormals = nsSmooth) then
- lastNormals^[i] := normal;
- end
- else if (FNormals = nsSmooth) then
- begin
- if invertNormals then
- begin
- topNormal := normal;
- bottomNormal := lastNormals^[i];
- end
- else
- begin
- topNormal := lastNormals^[i];
- bottomNormal := normal;
- end;
- lastNormals^[i] := normal;
- end;
- end;
- begin
- // to invert normals, we just need to flip top & bottom
- if invertNormals then
- begin
- ptBuffer := ptTop;
- ptTop := ptBottom;
- ptBottom := ptBuffer;
- end;
- // generate triangle strip for a level
- // TODO : support for triangle fans (when ptTop or ptBottom is on the Y Axis)
- alpha := startAlpha;
- i := 0;
- yOffset := startYOffset;
- topTPBase.S := 0;
- bottomTPBase.S := 0;
- topTPBase.T := topT;
- bottomTPBase.T := bottomT;
- VectorRotateAroundY(ptTop^, alpha, topBase);
- VectorRotateAroundY(ptBottom^, alpha, bottomBase);
- if gotYDeltaOffset then
- begin
- topBase.Y := topBase.Y + yOffset;
- bottomBase.Y := bottomBase.Y + yOffset;
- yOffset := yOffset + deltaYOffset;
- end;
- CalcNormal(@topBase, @bottomBase, normal);
- SetLocalNormals;
- inc(i);
- topTPNext := topTPBase;
- bottomTPNext := bottomTPBase;
- gl.Begin_(GL_TRIANGLE_STRIP);
- gl.Normal3fv(@topNormal);
- xgl.TexCoord2fv(@topTPBase);
- gl.Vertex3fv(@topBase);
- while alpha < stopAlpha do
- begin
- gl.Normal3fv(@bottomNormal);
- xgl.TexCoord2fv(@bottomTPBase);
- gl.Vertex3fv(@bottomBase);
- nextAlpha := alpha + deltaAlpha;
- topTPNext.S := topTPNext.S + deltaS;
- bottomTPNext.S := bottomTPNext.S + deltaS;
- VectorRotateAroundY(ptTop^, nextAlpha, topNext);
- VectorRotateAroundY(ptBottom^, nextAlpha, bottomNext);
- if gotYDeltaOffset then
- begin
- topNext.Y := topNext.Y + yOffset;
- bottomNext.Y := bottomNext.Y + yOffset;
- yOffset := yOffset + deltaYOffset
- end;
- CalcNormal(@topNext, @bottomNext, normal);
- SetLocalNormals;
- inc(i);
- xgl.TexCoord2fv(@topTPNext);
- gl.Normal3fv(@topNormal);
- gl.Vertex3fv(@topNext);
- alpha := nextAlpha;
- topBase := topNext;
- topTPBase := topTPNext;
- bottomBase := bottomNext;
- bottomTPBase := bottomTPNext;
- end;
- gl.Normal3fv(@bottomNormal);
- xgl.TexCoord2fv(@bottomTPBase);
- gl.Vertex3fv(@bottomBase);
- gl.End_;
- firstStep := False;
- end;
- var
- i, nbSteps, nbDivisions: Integer;
- splinePos, lastSplinePos, bary, polygonNormal: TAffineVector;
- f: Single;
- spline: TCubicSpline;
- invertedNormals: Boolean;
- polygon: TGLNodes;
- begin
- if (Nodes.Count > 1) and (FStopAngle > FStartAngle) then
- begin
- startAlpha := FStartAngle * cPIdiv180;
- stopAlpha := FStopAngle * cPIdiv180;
- nbSteps := Round(((stopAlpha - startAlpha) / (2 * PI)) * FSlices);
- // drop 0.1% to slice count to care for precision losses
- deltaAlpha := (stopAlpha - startAlpha) / (nbSteps * 0.999);
- deltaS := (stopAlpha - startAlpha) / (2 * PI * nbSteps);
- gotYDeltaOffset := FYOffsetPerTurn <> 0;
- if gotYDeltaOffset then
- deltaYOffset := (FYOffsetPerTurn * (stopAlpha - startAlpha) / (2 * PI)) /
- nbSteps
- else
- deltaYOffset := 0;
- startYOffset := YOffsetPerTurn * startAlpha / (2 * PI);
- invertedNormals := (FNormalDirection = ndInside);
- FTriangleCount := 0;
- // generate sides
- if (rspInside in FParts) or (rspOutside in FParts) then
- begin
- // allocate lastNormals buffer (if smoothing)
- if FNormals = nsSmooth then
- begin
- GetMem(lastNormals, (FSlices + 2) * SizeOf(TAffineVector));
- firstStep := True;
- end;
- // start working
- if rspInside in Parts then
- begin
- firstStep := True;
- if (Division < 2) or (SplineMode = lsmLines) then
- begin
- // standard line(s), draw directly
- for i := 0 to Nodes.Count - 2 do
- with Nodes[i] do
- begin
- BuildStep(PAffineVector(Nodes[i].AsAddress),
- PAffineVector(Nodes[i + 1].AsAddress), not invertedNormals,
- i / (Nodes.Count - 1), (i + 1) / (Nodes.Count - 1));
- end;
- FTriangleCount := nbSteps * Nodes.Count * 2;
- end
- else
- begin
- // cubic spline
- Spline := Nodes.CreateNewCubicSpline;
- Spline.SplineAffineVector(0, lastSplinePos);
- f := 1 / Division;
- nbDivisions := (Nodes.Count - 1) * Division;
- for i := 1 to nbDivisions do
- begin
- Spline.SplineAffineVector(i * f, splinePos);
- BuildStep(@lastSplinePos, @splinePos, not invertedNormals,
- (i - 1) / nbDivisions, i / nbDivisions);
- lastSplinePos := splinePos;
- end;
- Spline.Free;
- FTriangleCount := nbSteps * nbDivisions * 2;
- end;
- end;
- if rspOutside in Parts then
- begin
- firstStep := True;
- if (Division < 2) or (SplineMode = lsmLines) then
- begin
- // standard line(s), draw directly
- for i := 0 to Nodes.Count - 2 do
- with Nodes[i] do
- begin
- BuildStep(PAffineVector(Nodes[i].AsAddress),
- PAffineVector(Nodes[i + 1].AsAddress), invertedNormals,
- i / (Nodes.Count - 1), (i + 1) / (Nodes.Count - 1));
- end;
- FTriangleCount := nbSteps * Nodes.Count * 2;
- end
- else
- begin
- // cubic spline
- Spline := Nodes.CreateNewCubicSpline;
- Spline.SplineAffineVector(0, lastSplinePos);
- f := 1 / Division;
- nbDivisions := (Nodes.Count - 1) * Division;
- for i := 1 to nbDivisions do
- begin
- Spline.SplineAffineVector(i * f, splinePos);
- BuildStep(@lastSplinePos, @splinePos, invertedNormals,
- (i - 1) / nbDivisions, i / nbDivisions);
- lastSplinePos := splinePos;
- end;
- Spline.Free;
- FTriangleCount := nbSteps * nbDivisions * 2;
- end;
- end;
- if (rspInside in FParts) and (rspOutside in FParts) then
- FTriangleCount := FTriangleCount * 2;
- xgl.TexCoord2fv(@NullTexPoint);
- // release lastNormals buffer (if smoothing)
- if FNormals = nsSmooth then
- FreeMem(lastNormals);
- end;
- // tessellate start/stop polygons
- if (rspStartPolygon in FParts) or (rspStopPolygon in FParts) then
- begin
- bary := Nodes.Barycenter;
- bary.Y := 0;
- NormalizeVector(bary);
- // tessellate start polygon
- if rspStartPolygon in FParts then
- begin
- polygon := Nodes.CreateCopy(nil);
- with polygon do
- begin
- RotateAroundY(RadToDeg(startAlpha));
- Translate(AffineVectorMake(0, startYOffset, 0));
- if invertedNormals then
- alpha := startAlpha + PI / 2
- else
- alpha := startAlpha + PI + PI / 2;
- polygonNormal := VectorRotateAroundY(bary, alpha);
- if SplineMode = lsmLines then
- RenderTesselatedPolygon(False, @polygonNormal, 1)
- else
- RenderTesselatedPolygon(False, @polygonNormal, Division);
- Free;
- end;
- // estimated count
- FTriangleCount := FTriangleCount + Nodes.Count + (Nodes.Count shr 1);
- end;
- // tessellate stop polygon
- if rspStopPolygon in FParts then
- begin
- polygon := Nodes.CreateCopy(nil);
- with polygon do
- begin
- RotateAroundY(RadToDeg(stopAlpha));
- Translate(AffineVectorMake(0, startYOffset + (stopAlpha - startAlpha)
- * YOffsetPerTurn / (2 * PI), 0));
- if invertedNormals then
- alpha := stopAlpha + PI + PI / 2
- else
- alpha := stopAlpha + PI / 2;
- polygonNormal := VectorRotateAroundY(bary, alpha);
- if SplineMode = lsmLines then
- RenderTesselatedPolygon(False, @polygonNormal, 1)
- else
- RenderTesselatedPolygon(False, @polygonNormal, Division);
- Free;
- end;
- // estimated count
- FTriangleCount := FTriangleCount + Nodes.Count + (Nodes.Count shr 1);
- end;
- end;
- end;
- end;
- function TGLRevolutionSolid.AxisAlignedDimensionsUnscaled: TGLVector;
- var
- maxRadius: Single;
- maxHeight: Single;
- i: integer;
- begin
- maxRadius := 0;
- maxHeight := 0;
- if FAxisAlignedDimensionsCache.X < 0 then
- begin
- for i := 0 to Nodes.Count - 1 do
- begin
- maxHeight := MaxFloat(maxHeight, Abs(Nodes[i].Y));
- maxRadius := MaxFloat(maxRadius, Sqr(Nodes[i].X) + Sqr(Nodes[i].Z));
- end;
- maxRadius := sqrt(maxRadius);
- FAxisAlignedDimensionsCache.X := maxRadius;
- FAxisAlignedDimensionsCache.Y := maxHeight;
- FAxisAlignedDimensionsCache.Z := maxRadius;
- end;
- SetVector(Result, FAxisAlignedDimensionsCache);
- end;
- procedure TGLRevolutionSolid.StructureChanged;
- begin
- FAxisAlignedDimensionsCache.X := -1;
- inherited;
- end;
- // ------------------
- // ------------------ TGLPipeNode ------------------
- // ------------------
- constructor TGLPipeNode.Create(Collection: TCollection);
- begin
- inherited Create(Collection);
- FRadiusFactor := 1.0;
- FColor := TGLColor.CreateInitialized(Self, clrBlack, ColorChanged);
- FTexCoordT := 1.0;
- end;
-
- destructor TGLPipeNode.Destroy;
- begin
- FColor.Free;
- inherited Destroy;
- end;
- procedure TGLPipeNode.Assign(Source: TPersistent);
- begin
- if Source is TGLPipeNode then
- begin
- RadiusFactor := TGLPipeNode(Source).FRadiusFactor;
- Color.DirectColor := TGLPipeNode(Source).Color.DirectColor;
- TexCoordT := TGLPipeNode(Source).FTexCoordT;
- end;
- inherited;
- end;
- function TGLPipeNode.GetDisplayName: string;
- begin
- Result := Format('%s / rf = %.3f', [inherited GetDisplayName, RadiusFactor]);
- ;
- end;
- procedure TGLPipeNode.SetRadiusFactor(const val: Single);
- begin
- if FRadiusFactor <> val then
- begin
- FRadiusFactor := val;
- Changed(false);
- //(Collection as TGLNodes).NotifyChange;
- end;
- end;
- function TGLPipeNode.StoreRadiusFactor: Boolean;
- begin
- Result := (FRadiusFactor <> 1.0);
- end;
- function TGLPipeNode.StoreTexCoordT: Boolean;
- begin
- Result := (FTexCoordT <> 1.0);
- end;
- procedure TGLPipeNode.SetColor(const val: TGLColor);
- begin
- FColor.Assign(val);
- end;
- procedure TGLPipeNode.ColorChanged(sender: TObject);
- begin
- TGLPipeNodes(Collection).NotifyChange;
- end;
- // ------------------
- // ------------------ TGLPipeNodes ------------------
- // ------------------
- constructor TGLPipeNodes.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner, TGLPipeNode);
- end;
- procedure TGLPipeNodes.SetItems(index: Integer; const val: TGLPipeNode);
- begin
- inherited Items[index] := val;
- end;
- function TGLPipeNodes.GetItems(index: Integer): TGLPipeNode;
- begin
- Result := TGLPipeNode(inherited Items[index]);
- end;
- function TGLPipeNodes.Add: TGLPipeNode;
- begin
- Result := (inherited Add) as TGLPipeNode;
- end;
- function TGLPipeNodes.FindItemID(ID: Integer): TGLPipeNode;
- begin
- Result := (inherited FindItemID(ID)) as TGLPipeNode;
- end;
- // ------------------
- // ------------------ TGLPipe ------------------
- // ------------------
- constructor TGLPipe.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FSlices := 16;
- FParts := [ppOutside];
- FRadius := 1.0;
- FTriangleCount := 0;
- FTextCoordMode := ptcmDefault;
- FTextCoordTileS := 1;
- FTextCoordTileT := 1;
- FNormalMode := pnmDefault;
- FNormalSmoothAngle := 0;
- end;
- procedure TGLPipe.CreateNodes;
- begin
- FNodes := TGLPipeNodes.Create(Self);
- end;
- destructor TGLPipe.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TGLPipe.SetSlices(const val: Integer);
- begin
- if (val <> FSlices) and (val > 0) then
- begin
- FSlices := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetParts(const val: TPipeParts);
- begin
- if FParts <> val then
- begin
- FParts := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetRadius(const val: Single);
- begin
- if FRadius <> val then
- begin
- FRadius := val;
- StructureChanged;
- end;
- end;
- function TGLPipe.StoreRadius: Boolean;
- begin
- Result := (FRadius <> 1.0);
- end;
- function TGLPipe.StoreTextCoordTileS: Boolean;
- begin
- Result := (FTextCoordTileS <> 1.0);
- end;
- function TGLPipe.StoreTextCoordTileT: Boolean;
- begin
- Result := (FTextCoordTileT <> 1.0);
- end;
- procedure TGLPipe.SetNodesColorMode(const val: TPipeNodesColorMode);
- begin
- if val <> FNodesColorMode then
- begin
- FNodesColorMode := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetTextCoordMode(const val: TPipeTexCoordMode);
- begin
- if val <> FTextCoordMode then
- begin
- FTextCoordMode := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetTextCoordTileS(const val: Single);
- begin
- if val <> FTextCoordTileS then
- begin
- FTextCoordTileS := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetTextCoordTileT(const val: Single);
- begin
- if val <> FTextCoordTileT then
- begin
- FTextCoordTileT := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetNormalMode(const val: TPipeNormalMode);
- begin
- if val <> FNormalMode then
- begin
- FNormalMode := val;
- StructureChanged;
- end;
- end;
- procedure TGLPipe.SetNormalSmoothAngle(const val: Single);
- begin
- if val <> FNormalSmoothAngle then
- begin
- FNormalSmoothAngle := val;
- if NormalMode = pnmAdvanced then
- StructureChanged;
- end;
- end;
- procedure TGLPipe.Assign(Source: TPersistent);
- begin
- if Source is TGLPipe then
- begin
- Slices := TGLPipe(Source).Slices;
- Parts := TGLPipe(Source).Parts;
- Radius := TGLPipe(Source).Radius;
- NodesColorMode := TGLPipe(Source).NodesColorMode;
- TexCoordMode := TGLPipe(Source).TexCoordMode;
- TexCoordTileS := TGLPipe(Source).TexCoordTileS;
- TexCoordTileT := TGLPipe(Source).TexCoordTileT;
- end;
- inherited;
- end;
- var
- vSinCache, vCosCache: array of Single;
- procedure TGLPipe.BuildList(var rci: TGLRenderContextInfo);
- type
- TNodeData = record
- pos: TAffineVector;
- normal: TAffineVector;
- innormal: TAffineVector;
- sidedir: TVector3f;
- end;
- TRowData = record
- node: array of TNodeData;
- color: TGLColorVector;
- center: TVector3f;
- textcoordT: Single;
- end;
- PRowData = ^TRowData;
- const
- cPNCMtoEnum: array[pncmEmission..pncmAmbientAndDiffuse] of Cardinal =
- (GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_AMBIENT_AND_DIFFUSE);
- procedure CalculateRow(row: PRowData;
- const center, normal: TAffineVector; radius: Single);
- var
- i: Integer;
- vx, vy: TAffineVector;
- begin
- // attempt to use object's Z as Y vector
- VectorCrossProduct(ZVector, normal, vx);
- if VectorNorm(vx) < 1e-7 then
- begin
- // bad luck, the X vector will do (unless it's or normal that was null)
- if VectorNorm(normal) < 1e-7 then
- begin
- SetVector(vx, XVector);
- SetVector(vy, ZVector);
- end
- else
- begin
- VectorCrossProduct(XVector, normal, vx);
- NormalizeVector(vx);
- VectorCrossProduct(normal, vx, vy);
- end;
- end
- else
- begin
- NormalizeVector(vx);
- VectorCrossProduct(normal, vx, vy);
- end;
- NormalizeVector(vy);
- ScaleVector(vx, FRadius);
- ScaleVector(vy, FRadius);
- // generate the circle
- for i := 0 to High(row^.node) do
- begin
- row^.node[i].normal := VectorCombine(vx, vy, vCosCache[i], vSinCache[i]);
- row^.node[i].pos := VectorCombine(PAffineVector(@center)^,
- row^.node[i].normal, 1, radius);
- SetVector(row^.node[i].sidedir, 0, 0, 0);
- end;
- row^.center := center;
- end;
- procedure RenderDisk(row: PRowData;
- const center: TGLVector; const normal: TAffineVector;
- invert: Boolean; TextCoordTileS: Single);
- var
- i: Integer;
- begin
- begin
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@row^.color);
- // it was necessary to change build process to generate textcoords
- gl.Begin_(GL_TRIANGLE_STRIP);
- gl.Normal3fv(@normal);
- case TexCoordMode of
- ptcmDefault, ptcmManual:
- begin
- if invert then
- begin
- for i := 0 to High(row^.node) - 1 do
- begin
- gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 1);
- gl.Vertex3fv(@row^.node[i].pos);
- gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 0);
- gl.Vertex3fv(@center);
- end;
- gl.TexCoord2f(TextCoordTileS, 1);
- gl.Vertex3fv(@row^.node[High(row^.node)].pos);
- end
- else
- begin
- for i := High(row^.node) downto 1 do
- begin
- gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 0);
- gl.Vertex3fv(@row^.node[i].pos);
- gl.TexCoord2f(i / (High(row^.node)) * TextCoordTileS, 1);
- gl.Vertex3fv(@center);
- end;
- gl.TexCoord2f(0, 0);
- gl.Vertex3fv(@row^.node[0].pos);
- end;
- end;
- end;
- gl.End_;
- end;
- end;
- procedure CalculateSides(prevRow, curRow: PRowData; const trajvec: TVector3f);
- var
- j, k, m, n: Integer;
- deltaNormal, deltaPos: array of Double;
- smoothanglerad: Single;
- begin
- SetLength(deltanormal, Slices);
- SetLength(deltapos, Slices);
- for k := 0 to Slices - 1 do
- begin //rotate index for curRow
- deltanormal[k] := 0; //sum of difference for normal vector
- deltapos[k] := 0; //sum of difference for pos vector
- for j := 0 to Slices - 1 do
- begin //over all places
- n := (j + k) mod Slices;
- deltanormal[k] := deltanormal[k] + VectorSpacing(curRow^.node[n].normal,
- prevRow^.node[j].normal);
- deltapos[k] := deltapos[k] + VectorSpacing(curRow^.node[n].pos,
- prevRow^.node[j].pos);
- end;
- end;
- //Search minimum
- // only search in deltapos, if i would search in deltanormal,
- // the same index of minimum would be found
- m := 0;
- for k := 1 to Slices - 1 do
- if deltapos[m] > deltapos[k] then
- m := k;
- // rotate count
- for k := 1 to m do
- begin
- // rotate the values of curRow
- curRow^.node[Slices] := curRow^.node[0];
- System.Move(curRow^.node[1], curRow^.node[0], SizeOf(TNodeData) * Slices);
- curRow^.node[Slices] := curRow^.node[0];
- end;
- case NormalMode of
- pnmDefault:
- begin
- for j := 0 to Slices do
- begin
- curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
- prevRow.node[j].innormal := VectorNegate(prevRow.node[j].normal);
- end;
- end;
- pnmAdvanced:
- begin
- smoothanglerad := DegToRadian(NormalSmoothAngle);
- for j := 0 to Slices do
- begin
- curRow.node[j].sidedir :=
- VectorNormalize(VectorSubtract(curRow.node[j].pos,
- prevRow.node[j].pos));
- if VectorDotProduct(curRow.node[j].sidedir, prevRow.node[j].sidedir)
- < Cos(smoothanglerad) then
- begin
- if VectorDotProduct(curRow.node[j].sidedir, VectorNormalize(
- VectorSubtract(curRow.node[j].pos, curRow.center))) > 0.99 then
- begin
- curRow.node[j].normal :=
- VectorCrossProduct(curRow.node[j].sidedir,
- VectorCrossProduct(curRow.node[j].sidedir,
- VectorNormalize(trajvec)));
- prevRow.node[j].normal :=
- VectorCrossProduct(curRow.node[j].sidedir,
- VectorCrossProduct(curRow.node[j].sidedir,
- VectorNormalize(trajvec)));
- end
- else
- begin
- if VectorDotProduct(curRow.node[j].sidedir, VectorNormalize(
- VectorSubtract(curRow.node[j].pos, curRow.center))) < -0.99
- then
- begin
- curRow.node[j].normal := VectorCrossProduct(VectorCrossProduct
- (curRow.node[j].sidedir, VectorNormalize(trajvec)),
- curRow.node[j].sidedir);
- prevRow.node[j].normal := VectorCrossProduct(VectorCrossProduct
- (curRow.node[j].sidedir, VectorNormalize(trajvec)),
- curRow.node[j].sidedir);
- end
- else
- begin
- if VectorDotProduct(trajvec, curRow.node[j].sidedir) < 0 then
- begin
- curRow.node[j].normal :=
- VectorCrossProduct(VectorNormalize(VectorCrossProduct
- (VectorNormalize(VectorSubtract(curRow.node[j].pos,
- curRow.center)),
- curRow.node[j].sidedir)), curRow.node[j].sidedir);
- prevRow.node[j].normal :=
- VectorCrossProduct(VectorNormalize(VectorCrossProduct
- (VectorNormalize(VectorSubtract(prevRow.node[j].pos,
- prevRow.center)),
- curRow.node[j].sidedir)), curRow.node[j].sidedir);
- end
- else
- begin
- curRow.node[j].normal :=
- VectorCrossProduct(curRow.node[j].sidedir, VectorNormalize
- (VectorCrossProduct(VectorNormalize(VectorSubtract(curRow.node[j].pos, curRow.center)),
- curRow.node[j].sidedir)));
- prevRow.node[j].normal :=
- VectorCrossProduct(curRow.node[j].sidedir, VectorNormalize
- (VectorCrossProduct(VectorNormalize(VectorSubtract(prevRow.node[j].pos, prevRow.center)),
- curRow.node[j].sidedir)));
- end;
- end;
- if VectorLength(curRow.node[j].normal) = 0 then
- curRow.node[j].normal := prevRow.node[j].normal;
- if VectorLength(prevRow.node[j].normal) = 0 then
- prevRow.node[j].normal := curRow.node[j].normal;
- //compute inside normales
- curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
- prevRow.node[j].innormal :=
- VectorNegate(prevRow.node[j].normal);
- end;
- end
- else
- begin
- if VectorDotProduct(curRow.node[j].sidedir,
- VectorNormalize(VectorSubtract
- (curRow.node[j].pos, curRow.center))) > 0.99 then
- begin
- curRow.node[j].normal :=
- VectorCrossProduct(curRow.node[j].sidedir,
- VectorCrossProduct(curRow.node[j].sidedir,
- VectorNormalize(trajvec)));
- end
- else
- begin
- if VectorDotProduct(curRow.node[j].sidedir, VectorNormalize(
- VectorSubtract(curRow.node[j].pos, curRow.center))) < -0.99
- then
- begin
- curRow.node[j].normal := VectorCrossProduct(VectorCrossProduct
- (curRow.node[j].sidedir, VectorNormalize(trajvec)),
- curRow.node[j].sidedir);
- end
- else
- begin
- if VectorDotProduct(trajvec, curRow.node[j].sidedir) < 0 then
- begin
- curRow.node[j].normal :=
- VectorCrossProduct(VectorNormalize(VectorCrossProduct
- (VectorNormalize(VectorSubtract(curRow.node[j].pos,
- curRow.center)),
- curRow.node[j].sidedir)), curRow.node[j].sidedir);
- end
- else
- begin
- curRow.node[j].normal :=
- VectorCrossProduct(curRow.node[j].sidedir, VectorNormalize
- (VectorCrossProduct(VectorNormalize(VectorSubtract(curRow.node[j].pos,
- curRow.center)), curRow.node[j].sidedir)));
- end;
- end;
- //compute inside normales
- curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
- end;
- end;
- end;
- end;
- end;
- end;
- procedure RenderSides(prevRow, curRow: PRowData; TextCoordTileS,
- TextCoordTileT: Single; outside: Boolean);
- var
- j: Integer;
- begin
- begin
- gl.Begin_(GL_TRIANGLE_STRIP);
- if outside then
- begin
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@curRow^.color);
- gl.TexCoord2f(0, curRow^.textcoordT * TextCoordTileT);
- gl.Normal3fv(@curRow^.node[0].normal);
- gl.Vertex3fv(@curRow^.node[0].pos);
- for j := 0 to Slices - 1 do
- begin
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@prevRow^.color);
- gl.TexCoord2f(j / Slices * TextCoordTileS, prevRow^.textcoordT *
- TextCoordTileT);
- gl.Normal3fv(@prevRow^.node[j].normal);
- gl.Vertex3fv(@prevRow^.node[j].pos);
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@curRow^.color);
- gl.TexCoord2f((j + 1) / Slices * TextCoordTileS, curRow^.textcoordT *
- TextCoordTileT);
- gl.Normal3fv(@curRow^.node[j + 1].normal);
- gl.Vertex3fv(@curRow^.node[j + 1].pos);
- end;
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@prevRow^.color);
- gl.TexCoord2f(TextCoordTileS, prevRow^.textcoordT * TextCoordTileT);
- gl.Normal3fv(@prevRow^.node[Slices].normal);
- gl.Vertex3fv(@prevRow^.node[Slices].pos);
- end
- else
- begin
- for j := 0 to Slices do
- begin
- curRow.node[j].innormal := VectorNegate(curRow.node[j].normal);
- prevRow.node[j].innormal := VectorNegate(prevRow.node[j].normal);
- end;
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@prevRow^.color);
- gl.TexCoord2f(0, prevRow^.textcoordT * TextCoordTileT);
- gl.Normal3fv(@prevRow^.node[0].innormal);
- gl.Vertex3fv(@prevRow^.node[0].pos);
- for j := 0 to Slices - 1 do
- begin
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@curRow^.color);
- gl.TexCoord2f(j / Slices * TextCoordTileS, curRow^.textcoordT *
- TextCoordTileT);
- gl.Normal3fv(@curRow^.node[j].innormal);
- gl.Vertex3fv(@curRow^.node[j].pos);
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@prevRow^.color);
- gl.TexCoord2f((j + 1) / Slices * TextCoordTileS, prevRow^.textcoordT *
- TextCoordTileT);
- gl.Normal3fv(@prevRow^.node[j + 1].innormal);
- gl.Vertex3fv(@prevRow^.node[j + 1].pos);
- end;
- if NodesColorMode <> pncmNone then
- gl.Color4fv(@curRow^.color);
- gl.TexCoord2f(TextCoordTileS, curRow^.textcoordT * TextCoordTileT);
- gl.Normal3fv(@curRow^.node[Slices].innormal);
- gl.Vertex3fv(@curRow^.node[Slices].pos);
- end;
- gl.End_;
- end;
- end;
- var
- i, curRow, nbDivisions, k: Integer;
- normal, splinePos: TAffineVector;
- rows: array[0..1] of TRowData;
- ra: PFloatArray;
- posSpline, rSpline: TCubicSpline;
- f, t: Single;
- begin
- FTriangleCount := 0;
- if Nodes.Count = 0 then
- Exit;
- SetLength(rows[0].node, Slices + 1);
- SetLength(rows[1].node, Slices + 1);
- if (Length(vSinCache) <> Slices + 1) or (Length(vCosCache) <> Slices + 1) then
- begin
- SetLength(vSinCache, Slices + 1);
- SetLength(vCosCache, Slices + 1);
- PrepareSinCosCache(vSinCache, vCosCache, 0, 360);
- end;
- if (SplineMode = lsmCubicSpline) and (Nodes.Count > 1) then
- begin
- // creates position spline
- posSpline := Nodes.CreateNewCubicSpline;
- // creates radius spline
- GetMem(ra, SizeOf(TGLFloat) * Nodes.Count);
- for i := 0 to Nodes.Count - 1 do
- ra^[i] := TGLPipeNode(Nodes[i]).RadiusFactor;
- rSpline := TCubicSpline.Create(ra, nil, nil, nil, Nodes.Count);
- FreeMem(ra);
- normal := posSpline.SplineSlopeVector(0);
- end
- else
- begin
- normal := Nodes.Vector(0);
- posSpline := nil;
- rSpline := nil;
- end;
- if NodesColorMode <> pncmNone then
- begin
- gl.ColorMaterial(GL_FRONT_AND_BACK, cPNCMtoEnum[NodesColorMode]);
- rci.GLStates.Enable(stColorMaterial);
- end
- else
- rci.GLStates.Disable(stColorMaterial);
- CalculateRow(@rows[0], PAffineVector(@Nodes[0].AsVector)^, normal,
- TGLPipeNode(Nodes[0]).RadiusFactor);
- rows[0].color := TGLPipeNodes(Nodes)[0].Color.Color;
- case TexCoordMode of
- ptcmDefault: rows[0].textcoordT := 0;
- ptcmManual: rows[0].textcoordT := TGLPipeNode(Nodes[0]).TexCoordT;
- end;
- if ppStartDisk in Parts then
- begin
- NegateVector(normal);
- if ppOutside in Parts then
- begin
- RenderDisk(@rows[0], Nodes[0].AsVector, normal, True, TexCoordTileS);
- FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
- end;
- if ppInside in Parts then
- begin
- RenderDisk(@rows[0], Nodes[0].AsVector, VectorNegate(normal), False,
- TexCoordTileS);
- FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
- end;
- end;
- if (Nodes.Count > 1) then
- begin
- if SplineMode = lsmCubicSpline then
- begin
- f := 1 / Division;
- nbDivisions := (Nodes.Count - 1) * Division;
- for i := 1 to nbDivisions do
- begin
- t := i * f;
- posSpline.SplineAffineVector(t, splinePos);
- normal := posSpline.SplineSlopeVector(t);
- NormalizeVector(normal);
- curRow := (i and 1);
- CalculateRow(@rows[curRow], splinePos, normal,
- rSpline.SplineX(t));
- if NodesColorMode <> pncmNone then
- begin
- k := Trunc(t);
- if k < Nodes.Count - 1 then
- rows[curRow].color := VectorLerp(TGLPipeNodes(Nodes)[k].Color.Color,
- TGLPipeNodes(Nodes)[k + 1].Color.Color,
- Frac(t))
- else
- rows[curRow].color := TGLPipeNodes(Nodes)[k].Color.Color;
- end;
- //
- case TexCoordMode of
- ptcmDefault:
- begin
- k := Trunc(t);
- if k < Nodes.Count - 1 then
- rows[curRow].textcoordT := Lerp(k,
- k + 1,
- Frac(t))
- else
- rows[curRow].textcoordT := k;
- end;
- ptcmManual:
- begin
- k := Trunc(t);
- if k < Nodes.Count - 1 then
- rows[curRow].textcoordT := Lerp(TGLPipeNode(Nodes[k]).TexCoordT,
- TGLPipeNode(Nodes[k + 1]).TexCoordT,
- Frac(t))
- else
- rows[curRow].textcoordT := TGLPipeNode(Nodes[k]).TexCoordT;
- end;
- end;
- if (ppOutside in Parts) or (ppInside in Parts) then
- CalculateSides(@rows[curRow xor 1], @rows[curRow], normal);
- if ppOutside in Parts then
- RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
- TexCoordTileT, True);
- if ppInside in Parts then
- RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
- TexCoordTileT, False);
- end;
- i := nbDivisions * (Slices + 1) * 2;
- if ppOutside in Parts then
- Inc(FTriangleCount, i);
- if ppInside in Parts then
- Inc(FTriangleCount, i);
- end
- else
- begin
- for i := 1 to Nodes.Count - 1 do
- begin
- curRow := (i and 1);
- //Initialize Texture coordinates
- case TexCoordMode of
- ptcmDefault: rows[curRow].textcoordT := i;
- ptcmManual: rows[curRow].textcoordT :=
- TGLPipeNode(Nodes[i]).TexCoordT;
- end;
- CalculateRow(@rows[curRow], PAffineVector(@Nodes[i].AsVector)^,
- Nodes.Vector(i), TGLPipeNode(Nodes[i]).RadiusFactor);
- rows[curRow].color := TGLPipeNodes(Nodes)[i].Color.Color;
- if (ppOutside in Parts) or (ppInside in Parts) then
- CalculateSides(@rows[curRow xor 1], @rows[curRow], Nodes.Vector(i));
- if ppOutside in Parts then
- RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
- TexCoordTileT, True);
- if ppInside in Parts then
- RenderSides(@rows[curRow xor 1], @rows[curRow], TexCoordTileS,
- TexCoordTileT, False);
- end;
- i := Nodes.Count * (Slices + 1) * 2;
- if ppOutside in Parts then
- Inc(FTriangleCount, i);
- if ppInside in Parts then
- Inc(FTriangleCount, i);
- end;
- end;
- if ppStopDisk in Parts then
- begin
- i := Nodes.Count - 1;
- if SplineMode = lsmCubicSpline then
- normal := posSpline.SplineSlopeVector(Nodes.Count - 1)
- else
- normal := Nodes.Vector(i);
- CalculateRow(@rows[0], PAffineVector(@Nodes[i].AsVector)^, normal,
- TGLPipeNode(Nodes[i]).RadiusFactor);
- rows[0].color := TGLPipeNodes(Nodes)[i].Color.Color;
- if ppOutside in Parts then
- begin
- RenderDisk(@rows[0], Nodes[i].AsVector, normal, False, TexCoordTileS);
- FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
- end;
- if ppInside in Parts then
- begin
- RenderDisk(@rows[0], Nodes[i].AsVector, VectorNegate(normal), True,
- TexCoordTileS);
- FTriangleCount := FTriangleCount + Slices * 2; //Slices+1;
- end;
- end;
- if SplineMode = lsmCubicSpline then
- begin
- posSpline.Free;
- rSpline.Free;
- end;
- end;
- // ------------------
- // ------------------ TGLExtrusionSolid ------------------
- // ------------------
- procedure TGLExtrusionSolid.Assign(Source: TPersistent);
- begin
- if Source is TGLExtrusionSolid then
- begin
- FStacks := TGLExtrusionSolid(Source).FStacks;
- FNormals := TGLExtrusionSolid(Source).FNormals;
- FNormalDirection := TGLExtrusionSolid(Source).FNormalDirection;
- FParts := TGLExtrusionSolid(Source).FParts;
- end;
- inherited;
- end;
- procedure TGLExtrusionSolid.BuildList(var rci: TGLRenderContextInfo);
- var
- {deltaS,}deltaZ: Single;
- lastNormal: TAffineVector;
- procedure CalcNormal(const Top, Bottom: TAffineVector; var normal:
- TAffineVector);
- { extrusion is in Z direction, so the Z component of the normal vector is
- always zero. }
- {var
- p : TAffineVector;}
- begin
- normal.X := Bottom.Y - Top.Y;
- normal.Y := Top.X - Bottom.X;
- normal.Z := 0;
- NormalizeVector(normal);
- if FHeight < 0 then
- NegateVector(normal);
- (*
- p:=Top; p[2]:=p[2] + FHeight;
- CalcPlaneNormal(top,bottom,p,normal);
- *)
- end;
- procedure BuildStep(ptTop, ptBottom: TAffineVector; invertNormals: Boolean;
- topT, bottomT: Single);
- var
- step: Integer;
- topBase, topNext, bottomBase, bottomNext, normal, normTop, normBottom:
- TAffineVector;
- topTPBase, topTPNext, bottomTPBase, bottomTPNext: TTexPoint;
- ptBuffer: TAffineVector;
- angle: Double;
- dir: TAffineVector;
- begin
- // to invert normals, we just need to flip top & bottom
- if invertNormals then
- begin
- ptBuffer := ptTop;
- ptTop := ptBottom;
- ptBottom := ptBuffer;
- end;
- // generate triangle strip for a level
- // TODO : support for triangle fans (when ptTop or ptBottom is on the Y Axis)
- /// topTPBase.S:=0; bottomTPBase.S:=0;
- topTPBase.T := topT;
- bottomTPBase.T := bottomT;
- topBase := ptTop;
- bottomBase := ptBottom;
- CalcNormal(topBase, bottomBase, normal);
- if (FNormals = nsFlat) then
- lastNormal := normal
- else if (FNormals = nsSmooth) then
- begin
- angle := VectorDotProduct(normal, lastNormal);
- if (angle < FMinSmoothAngleCos) then
- begin
- lastNormal := normal;
- end;
- end;
- if invertNormals then
- begin
- normTop := Normal;
- normBottom := lastnormal;
- end
- else
- begin
- normTop := lastNormal;
- normBottom := normal;
- end;
- dir := VectorNormalize(VectorSubtract(bottomBase, topBase));
- topTPBase.S := VectorDotProduct(topBase, dir);
- topTPBase.T := topBase.Z;
- bottomTPBase.S := VectorDotProduct(bottomBase, dir);
- bottomTPBase.T := bottomBase.Z;
- lastNormal := normal;
- topNext := topBase;
- bottomNext := bottomBase;
- topTPNext := topTPBase;
- bottomTPNext := bottomTPBase;
- gl.Begin_(GL_TRIANGLE_STRIP);
- gl.Normal3fv(@normTop);
- xgl.TexCoord2fv(@topTPBase);
- gl.Vertex3fv(@topBase);
- for step := 1 to FStacks do
- begin
- gl.Normal3fv(@normBottom);
- xgl.TexCoord2fv(@bottomTPBase);
- gl.Vertex3fv(@bottomBase);
- topNext.Z := step * DeltaZ;
- bottomNext.Z := topNext.Z;
- topTPNext.T := topNext.Z;
- bottomTPNext.T := bottomNext.Z;
- xgl.TexCoord2fv(@topTPNext);
- gl.Normal3fv(@normTop);
- gl.Vertex3fv(@topNext);
- topBase := topNext;
- topTPBase := topTPNext;
- bottomBase := bottomNext;
- bottomTPBase := bottomTPNext;
- end;
- gl.Normal3fv(@normBottom);
- xgl.TexCoord2fv(@bottomTPBase);
- gl.Vertex3fv(@bottomBase);
- gl.End_;
- end;
- var
- n, i: Integer;
- invertedNormals: Boolean;
- normal: TAffineVector;
- begin
- if Outline.Count < 1 then
- Exit;
- deltaZ := FHeight / FStacks;
- // deltaS:=1/FStacks;
- invertedNormals := (FNormalDirection = ndInside);
- FTriangleCount := 0;
- // generate sides
- if (FHeight <> 0) and ((espInside in FParts) or (espOutside in FParts)) then
- begin
- for n := 0 to Outline.Count - 1 do
- begin
- with Outline.List[n] do
- if count > 1 then
- begin
- if espInside in Parts then
- begin
- CalcNormal(List^[count - 1], List^[0], lastNormal);
- if not InvertedNormals then
- NegateVector(lastNormal);
- for i := 0 to Count - 2 do
- begin
- BuildStep(List^[i], List^[i + 1], not invertedNormals,
- i / (Count - 1), (i + 1) / (Count - 1));
- end;
- BuildStep(List^[count - 1], List^[0], not invertedNormals, 1, 0);
- end;
- if espOutside in Parts then
- begin
- CalcNormal(List^[count - 1], List^[0], lastNormal);
- if InvertedNormals then
- NegateVector(lastNormal);
- for i := 0 to Count - 2 do
- begin
- BuildStep(List^[i], List^[i + 1], invertedNormals,
- i / (Count - 1), (i + 1) / (Count - 1));
- end;
- BuildStep(List^[count - 1], List^[0], invertedNormals, 1, 0);
- end;
- end;
- end;
- xgl.TexCoord2fv(@NullTexPoint);
- end;
- // tessellate start/stop polygons
- if (espStartPolygon in FParts) or (espStopPolygon in FParts) then
- begin
- normal := ContoursNormal;
- // tessellate stop polygon
- if espStopPolygon in FParts then
- begin
- gl.PushMatrix;
- gl.Translatef(0, 0, FHeight);
- RenderTesselatedPolygon(true, @normal, invertedNormals);
- gl.PopMatrix;
- end;
- // tessellate start polygon
- if espStartPolygon in FParts then
- begin
- NegateVector(normal);
- RenderTesselatedPolygon(true, @normal, not invertedNormals);
- end;
- end;
- end;
-
- constructor TGLExtrusionSolid.Create(AOwner: TComponent);
- begin
- inherited;
- FHeight := 1;
- FStacks := 1;
- FNormals := nsFlat;
- FNormalDirection := ndOutside;
- FParts := [espOutside];
- MinSmoothAngle := 5;
- FAxisAlignedDimensionsCache.X := -1;
- end;
-
- destructor TGLExtrusionSolid.Destroy;
- begin
- inherited;
- end;
- procedure TGLExtrusionSolid.SetHeight(const Value: TGLFloat);
- begin
- if (Value <> FHeight) then
- begin
- FHeight := Value;
- StructureChanged;
- end;
- end;
- procedure TGLExtrusionSolid.SetMinSmoothAngle(const Value: Single);
- var
- s, c: Single;
- begin
- FMinSmoothAngle := Value;
- SinCosine(Value * cPidiv180, s, c);
- FMinSmoothAngleCos := c;
- end;
- procedure TGLExtrusionSolid.SetNormalDirection(const val: TGLNormalDirection);
- begin
- if FNormalDirection <> val then
- begin
- FNormalDirection := val;
- StructureChanged;
- end;
- end;
- procedure TGLExtrusionSolid.SetNormals(const val: TGLNormalSmoothing);
- begin
- if FNormals <> val then
- begin
- FNormals := val;
- StructureChanged;
- end;
- end;
- procedure TGLExtrusionSolid.SetParts(const val: TGLExtrusionSolidParts);
- begin
- if FParts <> val then
- begin
- FParts := val;
- StructureChanged;
- end;
- end;
- procedure TGLExtrusionSolid.SetStacks(const val: Integer);
- begin
- if (val <> FStacks) and (val > 0) then
- begin
- FStacks := val;
- StructureChanged;
- end;
- end;
- function TGLExtrusionSolid.AxisAlignedDimensionsUnscaled: TGLVector;
- var
- dMin, dMax: TAffineVector;
- begin
- if FAxisAlignedDimensionsCache.X < 0 then
- begin
- Contours.GetExtents(dMin, dMax);
- FAxisAlignedDimensionsCache.X := MaxFloat(Abs(dMin.X), Abs(dMax.X));
- FAxisAlignedDimensionsCache.Y := MaxFloat(Abs(dMin.Y), Abs(dMax.Y));
- FAxisAlignedDimensionsCache.Z := MaxFloat(Abs(dMin.Z), Abs(dMax.Z +
- Height));
- end;
- SetVector(Result, FAxisAlignedDimensionsCache);
- end;
- procedure TGLExtrusionSolid.StructureChanged;
- begin
- FAxisAlignedDimensionsCache.X := -1;
- inherited;
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterClasses([TGLRevolutionSolid, TGLExtrusionSolid, TGLPipe]);
- end.
|