1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762 |
- //
- // The multimedia graphics platform GLScene https://github.com/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.
- *)
- interface
- {$I GLScene.inc}
- uses
- Winapi.OpenGL,
- System.Classes,
- System.SysUtils,
- System.Math,
- GLS.OpenGLTokens,
- GLS.Context,
- GLS.Objects,
- GLS.Scene,
- GLS.MultiPolygon,
- GLS.Color,
- GLS.VectorGeometry,
- GLS.RenderContextInfo,
- GLS.Nodes,
- GLS.State,
- GLS.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
- GLS.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.
|