12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.Extrusion;
- (*
- Extrusion objects are solids defined by the
- surface described by a moving curve.
- TODO:
- ur:
- Suggestion:
- All extrusion objects use actually the same kind of "parts",
- one common type should do.
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- Winapi.OpenGLext,
- System.SysUtils,
- System.Classes,
- System.Math,
- GXS.XOpenGL,
- Stage.VectorTypes,
- GXS.VectorLists,
- Stage.VectorGeometry,
- Stage.Spline,
- GXS.Context,
- GXS.Objects,
- GXS.Scene,
- GXS.MultiPolygon,
- GXS.Color,
- GXS.RenderContextInfo,
- GXS.Nodes,
- GXS.State;
- type
- TgxExtrusionSolidPart = (espOutside, espInside, espStartPolygon, espStopPolygon);
- TgxExtrusionSolidParts = set of TgxExtrusionSolidPart;
- TgxRevolutionSolidPart = (rspOutside, rspInside, rspStartPolygon, rspStopPolygon);
- TgxRevolutionSolidParts = set of TgxRevolutionSolidPart;
- (* 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 TgxRevolutionSolid 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. *)
- TgxRevolutionSolid = class(TgxPolygonBase)
- private
- FSlices: Integer;
- FStartAngle, FStopAngle: Single;
- FNormals: TgxNormalSmoothing;
- FYOffsetPerTurn: Single;
- FTriangleCount: Integer;
- FNormalDirection: TgxNormalDirection;
- FParts: TgxRevolutionSolidParts;
- FAxisAlignedDimensionsCache: TVector4f;
- protected
- procedure SetStartAngle(const val: Single);
- procedure SetStopAngle(const val: Single);
- function StoreStopAngle: Boolean;
- procedure SetSlices(const val: Integer);
- procedure SetNormals(const val: TgxNormalSmoothing);
- procedure SetYOffsetPerTurn(const val: Single);
- procedure SetNormalDirection(const val: TgxNormalDirection);
- procedure SetParts(const val: TgxRevolutionSolidParts);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var rci: TgxRenderContextInfo); override;
- // Number of triangles used for rendering.
- property TriangleCount: Integer read FTriangleCount;
- function AxisAlignedDimensionsUnscaled: TVector4f; 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: TgxRevolutionSolidParts 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: TgxNormalSmoothing read FNormals write SetNormals default nsFlat;
- property NormalDirection: TgxNormalDirection read FNormalDirection write SetNormalDirection default ndOutside;
- end;
- (*Extrudes a complex Polygon into Z direction.
- For contour description see TgxMultiPolygonBase.
- properties Parts, Height (or should we better cal it Depth, because its in Z?),
- Stacks, Normals and NormalDirection are equivalent to TgxRevolutionSolid.
- 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. *)
- TgxExtrusionSolid = class(TgxMultiPolygonBase)
- private
- FStacks: Integer;
- FNormals: TgxNormalSmoothing;
- FTriangleCount: Integer;
- FNormalDirection: TgxNormalDirection;
- FParts: TgxExtrusionSolidParts;
- FHeight: Single;
- FMinSmoothAngle: Single;
- FMinSmoothAngleCos: Single;
- FAxisAlignedDimensionsCache: TVector4f;
- procedure SetHeight(const Value: Single);
- procedure SetMinSmoothAngle(const Value: Single);
- protected
- procedure SetStacks(const val: Integer);
- procedure SetNormals(const val: TgxNormalSmoothing);
- procedure SetNormalDirection(const val: TgxNormalDirection);
- procedure SetParts(const val: TgxExtrusionSolidParts);
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure BuildList(var rci: TgxRenderContextInfo); override;
- // Number of triangles used for rendering.
- property TriangleCount: Integer read FTriangleCount;
- function AxisAlignedDimensionsUnscaled: TVector4f; override;
- procedure StructureChanged; override;
- published
- property Parts: TgxExtrusionSolidParts read FParts write SetParts default [espOutside];
- property Height: Single read FHeight write SetHeight;
- property Stacks: Integer read FStacks write SetStacks default 1;
- property Normals: TgxNormalSmoothing read FNormals write SetNormals default nsFlat;
- property NormalDirection: TgxNormalDirection read FNormalDirection write SetNormalDirection default ndOutside;
- property MinSmoothAngle: Single read FMinSmoothAngle write SetMinSmoothAngle;
- end;
- TgxPipeNode = class(TgxNode)
- private
- FRadiusFactor: Single;
- FColor: TgxColor;
- FTexCoordT: Single;
- protected
- function GetDisplayName: string; override;
- procedure SetRadiusFactor(const val: Single);
- function StoreRadiusFactor: Boolean;
- procedure SetColor(const val: TgxColor);
- 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: TgxColor read FColor write SetColor;
- property TexCoordT: Single read FTexCoordT write FTexCoordT stored StoreTexCoordT;
- end;
- TgxPipeNodes = class(TgxLinesNodes)
- protected
- procedure SetItems(index: Integer; const val: TgxPipeNode);
- function GetItems(index: Integer): TgxPipeNode;
- public
- constructor Create(AOwner: TComponent);
- function Add: TgxPipeNode;
- function FindItemID(ID: Integer): TgxPipeNode;
- property Items[index: Integer]: TgxPipeNode 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. *)
- TgxPipe = class(TgxPolygonBase)
- 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: TgxRenderContextInfo); 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
- // ------------------------------------------------------------------
- // ------------------
- // ------------------ TgxRevolutionSolid ------------------
- // ------------------
- constructor TgxRevolutionSolid.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FStartAngle := 0;
- FStopAngle := 360;
- FSlices := 16;
- FNormals := nsFlat;
- FNormalDirection := ndOutside;
- FParts := [rspOutside];
- end;
- destructor TgxRevolutionSolid.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TgxRevolutionSolid.SetStartAngle(const val: Single);
- begin
- if FStartAngle <> val then
- begin
- FStartAngle := val;
- if FStartAngle > FStopAngle then
- FStopAngle := FStartAngle;
- StructureChanged;
- end;
- end;
- procedure TgxRevolutionSolid.SetStopAngle(const val: Single);
- begin
- if FStopAngle <> val then
- begin
- FStopAngle := val;
- if FStopAngle < FStartAngle then
- FStartAngle := FStopAngle;
- StructureChanged;
- end;
- end;
- function TgxRevolutionSolid.StoreStopAngle: Boolean;
- begin
- Result := (FStopAngle <> 360);
- end;
- procedure TgxRevolutionSolid.SetSlices(const val: Integer);
- begin
- if (val <> FSlices) and (val > 0) then
- begin
- FSlices := val;
- StructureChanged;
- end;
- end;
- procedure TgxRevolutionSolid.SetNormals(const val: TgxNormalSmoothing);
- begin
- if FNormals <> val then
- begin
- FNormals := val;
- StructureChanged;
- end;
- end;
- procedure TgxRevolutionSolid.SetYOffsetPerTurn(const val: Single);
- begin
- if FYOffsetPerTurn <> val then
- begin
- FYOffsetPerTurn := val;
- StructureChanged;
- end;
- end;
- procedure TgxRevolutionSolid.SetNormalDirection(const val: TgxNormalDirection);
- begin
- if FNormalDirection <> val then
- begin
- FNormalDirection := val;
- StructureChanged;
- end;
- end;
- procedure TgxRevolutionSolid.SetParts(const val: TgxRevolutionSolidParts);
- begin
- if FParts <> val then
- begin
- FParts := val;
- StructureChanged;
- end;
- end;
- procedure TgxRevolutionSolid.Assign(Source: TPersistent);
- begin
- if Source is TgxRevolutionSolid then
- begin
- FStartAngle := TgxRevolutionSolid(Source).FStartAngle;
- FStopAngle := TgxRevolutionSolid(Source).FStopAngle;
- FSlices := TgxRevolutionSolid(Source).FSlices;
- FNormals := TgxRevolutionSolid(Source).FNormals;
- FYOffsetPerTurn := TgxRevolutionSolid(Source).FYOffsetPerTurn;
- FNormalDirection := TgxRevolutionSolid(Source).FNormalDirection;
- FParts := TgxRevolutionSolid(Source).FParts;
- end;
- inherited Assign(Source);
- end;
- procedure TgxRevolutionSolid.BuildList(var rci: TgxRenderContextInfo);
- 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;
- glBegin(GL_TRIANGLE_STRIP);
- glNormal3fv(@topNormal);
- glTexCoord2fv(@topTPBase);
- glVertex3fv(@topBase);
- while alpha < stopAlpha do
- begin
- glNormal3fv(@bottomNormal);
- glTexCoord2fv(@bottomTPBase);
- glVertex3fv(@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);
- glTexCoord2fv(@topTPNext);
- glNormal3fv(@topNormal);
- glVertex3fv(@topNext);
- alpha := nextAlpha;
- topBase := topNext;
- topTPBase := topTPNext;
- bottomBase := bottomNext;
- bottomTPBase := bottomTPNext;
- end;
- glNormal3fv(@bottomNormal);
- glTexCoord2fv(@bottomTPBase);
- glVertex3fv(@bottomBase);
- glEnd;
- firstStep := False;
- end;
- var
- i, nbSteps, nbDivisions: Integer;
- splinePos, lastSplinePos, bary, polygonNormal: TAffineVector;
- f: Single;
- Spline: TCubicSpline;
- invertedNormals: Boolean;
- polygon: TgxNodes;
- 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;
- glTexCoord2fv(@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(RadianToDeg(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(RadianToDeg(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 TgxRevolutionSolid.AxisAlignedDimensionsUnscaled: TVector4f;
- 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 TgxRevolutionSolid.StructureChanged;
- begin
- FAxisAlignedDimensionsCache.X := -1;
- inherited;
- end;
- // ------------------
- // ------------------ TgxPipeNode ------------------
- // ------------------
- constructor TgxPipeNode.Create(Collection: TCollection);
- begin
- inherited Create(Collection);
- FRadiusFactor := 1.0;
- FColor := TgxColor.CreateInitialized(Self, clrBlack, ColorChanged);
- FTexCoordT := 1.0;
- end;
- destructor TgxPipeNode.Destroy;
- begin
- FColor.Free;
- inherited Destroy;
- end;
- procedure TgxPipeNode.Assign(Source: TPersistent);
- begin
- if Source is TgxPipeNode then
- begin
- RadiusFactor := TgxPipeNode(Source).FRadiusFactor;
- Color.DirectColor := TgxPipeNode(Source).Color.DirectColor;
- TexCoordT := TgxPipeNode(Source).FTexCoordT;
- end;
- inherited;
- end;
- function TgxPipeNode.GetDisplayName: string;
- begin
- Result := Format('%s / rf = %.3f', [inherited GetDisplayName, RadiusFactor]);;
- end;
- procedure TgxPipeNode.SetRadiusFactor(const val: Single);
- begin
- if FRadiusFactor <> val then
- begin
- FRadiusFactor := val;
- Changed(False);
- // (Collection as TgxNodes).NotifyChange;
- end;
- end;
- function TgxPipeNode.StoreRadiusFactor: Boolean;
- begin
- Result := (FRadiusFactor <> 1.0);
- end;
- function TgxPipeNode.StoreTexCoordT: Boolean;
- begin
- Result := (FTexCoordT <> 1.0);
- end;
- procedure TgxPipeNode.SetColor(const val: TgxColor);
- begin
- FColor.Assign(val);
- end;
- procedure TgxPipeNode.ColorChanged(sender: TObject);
- begin
- TgxPipeNodes(Collection).NotifyChange;
- end;
- // ------------------
- // ------------------ TgxPipeNodes ------------------
- // ------------------
- constructor TgxPipeNodes.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner, TgxPipeNode);
- end;
- procedure TgxPipeNodes.SetItems(index: Integer; const val: TgxPipeNode);
- begin
- inherited Items[index] := val;
- end;
- function TgxPipeNodes.GetItems(index: Integer): TgxPipeNode;
- begin
- Result := TgxPipeNode(inherited Items[index]);
- end;
- function TgxPipeNodes.Add: TgxPipeNode;
- begin
- Result := (inherited Add) as TgxPipeNode;
- end;
- function TgxPipeNodes.FindItemID(ID: Integer): TgxPipeNode;
- begin
- Result := (inherited FindItemID(ID)) as TgxPipeNode;
- end;
- // ------------------
- // ------------------ TgxPipe ------------------
- // ------------------
- constructor TgxPipe.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 TgxPipe.CreateNodes;
- begin
- FNodes := TgxPipeNodes.Create(Self);
- end;
- destructor TgxPipe.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TgxPipe.SetSlices(const val: Integer);
- begin
- if (val <> FSlices) and (val > 0) then
- begin
- FSlices := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetParts(const val: TPipeParts);
- begin
- if FParts <> val then
- begin
- FParts := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetRadius(const val: Single);
- begin
- if FRadius <> val then
- begin
- FRadius := val;
- StructureChanged;
- end;
- end;
- function TgxPipe.StoreRadius: Boolean;
- begin
- Result := (FRadius <> 1.0);
- end;
- function TgxPipe.StoreTextCoordTileS: Boolean;
- begin
- Result := (FTextCoordTileS <> 1.0);
- end;
- function TgxPipe.StoreTextCoordTileT: Boolean;
- begin
- Result := (FTextCoordTileT <> 1.0);
- end;
- procedure TgxPipe.SetNodesColorMode(const val: TPipeNodesColorMode);
- begin
- if val <> FNodesColorMode then
- begin
- FNodesColorMode := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetTextCoordMode(const val: TPipeTexCoordMode);
- begin
- if val <> FTextCoordMode then
- begin
- FTextCoordMode := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetTextCoordTileS(const val: Single);
- begin
- if val <> FTextCoordTileS then
- begin
- FTextCoordTileS := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetTextCoordTileT(const val: Single);
- begin
- if val <> FTextCoordTileT then
- begin
- FTextCoordTileT := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetNormalMode(const val: TPipeNormalMode);
- begin
- if val <> FNormalMode then
- begin
- FNormalMode := val;
- StructureChanged;
- end;
- end;
- procedure TgxPipe.SetNormalSmoothAngle(const val: Single);
- begin
- if val <> FNormalSmoothAngle then
- begin
- FNormalSmoothAngle := val;
- if NormalMode = pnmAdvanced then
- StructureChanged;
- end;
- end;
- procedure TgxPipe.Assign(Source: TPersistent);
- begin
- if Source is TgxPipe then
- begin
- Slices := TgxPipe(Source).Slices;
- Parts := TgxPipe(Source).Parts;
- Radius := TgxPipe(Source).Radius;
- NodesColorMode := TgxPipe(Source).NodesColorMode;
- TexCoordMode := TgxPipe(Source).TexCoordMode;
- TexCoordTileS := TgxPipe(Source).TexCoordTileS;
- TexCoordTileT := TgxPipe(Source).TexCoordTileT;
- end;
- inherited;
- end;
- var
- vSinCache, vCosCache: array of Single;
- procedure TgxPipe.BuildList(var rci: TgxRenderContextInfo);
- type
- TNodeData = record
- pos: TAffineVector;
- normal: TAffineVector;
- innormal: TAffineVector;
- sidedir: TVector3f;
- end;
- TRowData = record
- node: array of TNodeData;
- Color: TgxColorVector;
- center: TVector3f;
- textcoordT: Single;
- end;
- PRowData = ^TRowData;
- const
- cPNCMtoEnum: array [pncmEmission .. pncmAmbientAndDiffuse] of GLEnum = (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: TVector4f; const normal: TAffineVector; invert: Boolean;
- TextCoordTileS: Single);
- var
- i: Integer;
- begin
- begin
- if NodesColorMode <> pncmNone then
- glColor4fv(@row^.Color);
- // it was necessary to change build process to generate textcoords
- glBegin(GL_TRIANGLE_STRIP);
- glNormal3fv(@normal);
- case TexCoordMode of
- ptcmDefault, ptcmManual:
- begin
- if invert then
- begin
- for i := 0 to High(row^.node) - 1 do
- begin
- glTexCoord2f(i / (High(row^.node)) * TextCoordTileS, 1);
- glVertex3fv(@row^.node[i].pos);
- glTexCoord2f(i / (High(row^.node)) * TextCoordTileS, 0);
- glVertex3fv(@center);
- end;
- glTexCoord2f(TextCoordTileS, 1);
- glVertex3fv(@row^.node[High(row^.node)].pos);
- end
- else
- begin
- for i := High(row^.node) downto 1 do
- begin
- glTexCoord2f(i / (High(row^.node)) * TextCoordTileS, 0);
- glVertex3fv(@row^.node[i].pos);
- glTexCoord2f(i / (High(row^.node)) * TextCoordTileS, 1);
- glVertex3fv(@center);
- end;
- glTexCoord2f(0, 0);
- glVertex3fv(@row^.node[0].pos);
- end;
- end;
- end;
- glEnd;
- end;
- end;
- procedure CalculateSides(prevRow, curRow: PRowData; 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
- glBegin(GL_TRIANGLE_STRIP);
- if outside then
- begin
- if NodesColorMode <> pncmNone then
- glColor4fv(@curRow^.Color);
- glTexCoord2f(0, curRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@curRow^.node[0].normal);
- glVertex3fv(@curRow^.node[0].pos);
- for j := 0 to Slices - 1 do
- begin
- if NodesColorMode <> pncmNone then
- glColor4fv(@prevRow^.Color);
- glTexCoord2f(j / Slices * TextCoordTileS, prevRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@prevRow^.node[j].normal);
- glVertex3fv(@prevRow^.node[j].pos);
- if NodesColorMode <> pncmNone then
- glColor4fv(@curRow^.Color);
- glTexCoord2f((j + 1) / Slices * TextCoordTileS, curRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@curRow^.node[j + 1].normal);
- glVertex3fv(@curRow^.node[j + 1].pos);
- end;
- if NodesColorMode <> pncmNone then
- glColor4fv(@prevRow^.Color);
- glTexCoord2f(TextCoordTileS, prevRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@prevRow^.node[Slices].normal);
- glVertex3fv(@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
- glColor4fv(@prevRow^.Color);
- glTexCoord2f(0, prevRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@prevRow^.node[0].innormal);
- glVertex3fv(@prevRow^.node[0].pos);
- for j := 0 to Slices - 1 do
- begin
- if NodesColorMode <> pncmNone then
- glColor4fv(@curRow^.Color);
- glTexCoord2f(j / Slices * TextCoordTileS, curRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@curRow^.node[j].innormal);
- glVertex3fv(@curRow^.node[j].pos);
- if NodesColorMode <> pncmNone then
- glColor4fv(@prevRow^.Color);
- glTexCoord2f((j + 1) / Slices * TextCoordTileS, prevRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@prevRow^.node[j + 1].innormal);
- glVertex3fv(@prevRow^.node[j + 1].pos);
- end;
- if NodesColorMode <> pncmNone then
- glColor4fv(@curRow^.Color);
- glTexCoord2f(TextCoordTileS, curRow^.textcoordT * TextCoordTileT);
- glNormal3fv(@curRow^.node[Slices].innormal);
- glVertex3fv(@curRow^.node[Slices].pos);
- end;
- glEnd;
- 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
- // create position spline
- posSpline := Nodes.CreateNewCubicSpline;
- // create radius spline
- GetMem(ra, SizeOf(Single) * Nodes.Count);
- for i := 0 to Nodes.Count - 1 do
- ra^[i] := TgxPipeNode(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
- glColorMaterial(GL_FRONT_AND_BACK, cPNCMtoEnum[NodesColorMode]);
- rci.gxStates.Enable(stColorMaterial);
- end
- else
- rci.gxStates.Disable(stColorMaterial);
- CalculateRow(@rows[0], PAffineVector(@Nodes[0].AsVector)^, normal, TgxPipeNode(Nodes[0]).RadiusFactor);
- rows[0].Color := TgxPipeNodes(Nodes)[0].Color.Color;
- case TexCoordMode of
- ptcmDefault:
- rows[0].textcoordT := 0;
- ptcmManual:
- rows[0].textcoordT := TgxPipeNode(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(TgxPipeNodes(Nodes)[k].Color.Color,
- TgxPipeNodes(Nodes)[k + 1].Color.Color, Frac(T))
- else
- rows[curRow].Color := TgxPipeNodes(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(TgxPipeNode(Nodes[k]).TexCoordT, TgxPipeNode(Nodes[k + 1]).TexCoordT, Frac(T))
- else
- rows[curRow].textcoordT := TgxPipeNode(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 := TgxPipeNode(Nodes[i]).TexCoordT;
- end;
- CalculateRow(@rows[curRow], PAffineVector(@Nodes[i].AsVector)^, Nodes.Vector(i), TgxPipeNode(Nodes[i]).RadiusFactor);
- rows[curRow].Color := TgxPipeNodes(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, TgxPipeNode(Nodes[i]).RadiusFactor);
- rows[0].Color := TgxPipeNodes(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;
- // ------------------
- // ------------------ TgxExtrusionSolid ------------------
- // ------------------
- procedure TgxExtrusionSolid.Assign(Source: TPersistent);
- begin
- if Source is TgxExtrusionSolid then
- begin
- FStacks := TgxExtrusionSolid(Source).FStacks;
- FNormals := TgxExtrusionSolid(Source).FNormals;
- FNormalDirection := TgxExtrusionSolid(Source).FNormalDirection;
- FParts := TgxExtrusionSolid(Source).FParts;
- end;
- inherited;
- end;
- procedure TgxExtrusionSolid.BuildList(var rci: TgxRenderContextInfo);
- 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;
- glBegin(GL_TRIANGLE_STRIP);
- glNormal3fv(@normTop);
- glTexCoord2fv(@topTPBase);
- glVertex3fv(@topBase);
- for step := 1 to FStacks do
- begin
- glNormal3fv(@normBottom);
- glTexCoord2fv(@bottomTPBase);
- glVertex3fv(@bottomBase);
- topNext.Z := step * deltaZ;
- bottomNext.Z := topNext.Z;
- topTPNext.T := topNext.Z;
- bottomTPNext.T := bottomNext.Z;
- glTexCoord2fv(@topTPNext);
- glNormal3fv(@normTop);
- glVertex3fv(@topNext);
- topBase := topNext;
- topTPBase := topTPNext;
- bottomBase := bottomNext;
- bottomTPBase := bottomTPNext;
- end;
- glNormal3fv(@normBottom);
- glTexCoord2fv(@bottomTPBase);
- glVertex3fv(@bottomBase);
- glEnd;
- 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;
- glTexCoord2fv(@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
- glPushMatrix;
- glTranslatef(0, 0, FHeight);
- RenderTesselatedPolygon(True, @normal, invertedNormals);
- glPopMatrix;
- end;
- // tessellate start polygon
- if espStartPolygon in FParts then
- begin
- NegateVector(normal);
- RenderTesselatedPolygon(True, @normal, not invertedNormals);
- end;
- end;
- end;
- constructor TgxExtrusionSolid.Create(AOwner: TComponent);
- begin
- inherited;
- FHeight := 1;
- FStacks := 1;
- FNormals := nsFlat;
- FNormalDirection := ndOutside;
- FParts := [espOutside];
- MinSmoothAngle := 5;
- FAxisAlignedDimensionsCache.X := -1;
- end;
- destructor TgxExtrusionSolid.Destroy;
- begin
- inherited;
- end;
- procedure TgxExtrusionSolid.SetHeight(const Value: Single);
- begin
- if (Value <> FHeight) then
- begin
- FHeight := Value;
- StructureChanged;
- end;
- end;
- procedure TgxExtrusionSolid.SetMinSmoothAngle(const Value: Single);
- var
- S, c: Single;
- begin
- FMinSmoothAngle := Value;
- SinCosine(Value * cPIdiv180, S, c);
- FMinSmoothAngleCos := c;
- end;
- procedure TgxExtrusionSolid.SetNormalDirection(const val: TgxNormalDirection);
- begin
- if FNormalDirection <> val then
- begin
- FNormalDirection := val;
- StructureChanged;
- end;
- end;
- procedure TgxExtrusionSolid.SetNormals(const val: TgxNormalSmoothing);
- begin
- if FNormals <> val then
- begin
- FNormals := val;
- StructureChanged;
- end;
- end;
- procedure TgxExtrusionSolid.SetParts(const val: TgxExtrusionSolidParts);
- begin
- if FParts <> val then
- begin
- FParts := val;
- StructureChanged;
- end;
- end;
- procedure TgxExtrusionSolid.SetStacks(const val: Integer);
- begin
- if (val <> FStacks) and (val > 0) then
- begin
- FStacks := val;
- StructureChanged;
- end;
- end;
- function TgxExtrusionSolid.AxisAlignedDimensionsUnscaled: TVector4f;
- 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 TgxExtrusionSolid.StructureChanged;
- begin
- FAxisAlignedDimensionsCache.X := -1;
- inherited;
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterClasses([TgxRevolutionSolid, TgxExtrusionSolid, TgxPipe]);
- end.
|