123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.Trail;
- (*
- Creates a trail-like mesh.
- Based on Jason Lanford's demo.
- *)
- interface
- {$I Stage.Defines.inc}
- uses
- System.Classes,
- System.SysUtils,
- GXS.Scene,
- Stage.VectorTypes,
- GXS.MeshUtils,
- Stage.VectorGeometry,
- GXS.VectorFileObjects,
- GXS.Mesh,
- GXS.Objects,
- GXS.Material,
- Stage.Strings,
- GXS.BaseClasses;
- const
- cMaxVerts = 2000;
- type
- TMarkStyle = (msUp, msDirection, msFaceCamera, msRight);
- TgxTrail = class(TgxMesh)
- private
- fVertLimit: integer;
- fTimeLimit: single;
- fMinDistance: single;
- fAlpha: single;
- fAlphaFade: Boolean;
- fUVScale: single;
- fVerts: array [1 .. cMaxVerts] of TVector3f;
- fUVs: array [1 .. cMaxVerts] of TTexpoint;
- fTimeStamps: array [1 .. cMaxVerts] of Double;
- fVertStart, fVertEnd, fVertCount: integer;
- fLastV0Pos, fLastPos, fLastDir, fLastUp: TVector3f;
- FLastUVs: single;
- // used for UV scaling
- fLastP1, fLastP2: TVector3f;
- FTrailObject: TgxBaseSceneObject;
- FMarkStyle: TMarkStyle;
- FMarkWidth: single;
- FEnabled: Boolean;
- FAntiZFightOffset: single;
- procedure SetTrailObject(const Value: TgxBaseSceneObject);
- procedure SetMarkStyle(const Value: TMarkStyle);
- procedure SetAlpha(const Value: single);
- procedure SetAlphaFade(const Value: Boolean);
- procedure SetMinDistance(const Value: single);
- procedure SetTimeLimit(const Value: single);
- procedure SetUVScale(const Value: single);
- procedure SetVertLimit(const Value: integer);
- procedure SetMarkWidth(const Value: single);
- procedure SetEnabled(const Value: Boolean);
- function StoreAntiZFightOffset: Boolean;
- protected
- procedure Notification(AComponent: TComponent;
- Operation: TOperation); override;
- public
- // EnableUVmapping: boolean; // generate UV's or not
- procedure DoProgress(const progressTime: TgxProgressTimes); override;
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure CreateMark(obj: TgxBaseSceneObject; width: single;
- CurrentTime: Double); overload;
- procedure CreateMark(APos, ADir, AUp: TVector3f; AWidth: single;
- ACurrentTime: Double); overload;
- function CreateMark(p1, p2: TVector3f; CurrentTime: Double)
- : Boolean; overload;
- procedure ClearMarks;
- published
- (* Add a tiny bit of offset to help prevent z-fighting..
- Need a better solution here as this will get out of whack on really
- long trails and is dependant on scene scale. *)
- property AntiZFightOffset: single read FAntiZFightOffset
- write FAntiZFightOffset stored StoreAntiZFightOffset;
- property VertLimit: integer read fVertLimit write SetVertLimit default 150;
- property TimeLimit: single read fTimeLimit write SetTimeLimit;
- { Don't create mark unless moved at least this distance. }
- property MinDistance: single read fMinDistance write SetMinDistance;
- property Alpha: single read fAlpha write SetAlpha;
- property AlphaFade: Boolean read fAlphaFade write SetAlphaFade default True;
- property UVScale: single read fUVScale write SetUVScale;
- property MarkStyle: TMarkStyle read FMarkStyle write SetMarkStyle
- default msFaceCamera;
- property TrailObject: TgxBaseSceneObject read FTrailObject
- write SetTrailObject default nil;
- property MarkWidth: single read FMarkWidth write SetMarkWidth;
- property Enabled: Boolean read FEnabled write SetEnabled default True;
- end;
- // -----------------------------------------------------------------------------
- implementation
- // -----------------------------------------------------------------------------
- constructor TgxTrail.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- vertices.Clear; // inherited Tgxmesh makes a triangle... remove it.
- Mode := mmTriangleStrip;
- FAntiZFightOffset := 0.0000266;
- VertexMode := vmVNCT;
- fVertStart := 1;
- fVertEnd := 0;
- fVertCount := 0;
- fVertLimit := 150;
- fTimeLimit := 0.5;
- fMinDistance := 0.05;
- fAlphaFade := True;
- fAlpha := 1.0;
- FLastUVs := 0;
- fUVScale := 1.0;
- FMarkWidth := 0.5;
- FEnabled := True;
- FMarkStyle := msFaceCamera;
- Material.BlendingMode := bmAdditive;
- Material.FaceCulling := fcNoCull;
- end;
- destructor TgxTrail.Destroy;
- begin
- // notta?
- inherited Destroy;
- end;
- procedure TgxTrail.DoProgress(const progressTime: TgxProgressTimes);
- begin
- inherited;
- if Enabled and Assigned(TrailObject) then
- begin
- CreateMark(TrailObject, MarkWidth, progressTime.NewTime);
- end;
- end;
- procedure TgxTrail.ClearMarks;
- begin
- vertices.Clear;
- fVertCount := 0;
- fVertEnd := 0;
- fVertStart := 1;
- end;
- procedure TgxTrail.CreateMark(obj: TgxBaseSceneObject; width: single;
- CurrentTime: Double);
- var
- v0, dv, p1, p2: TVector3f;
- v: TVector3f;
- c: TgxCamera;
- begin
- case MarkStyle of
- msUp:
- begin
- v := AffinevectorMake(obj.AbsoluteUp);
- end;
- msDirection:
- begin
- v := AffinevectorMake(obj.AbsoluteDirection);
- end;
- msRight:
- begin
- v := AffinevectorMake(obj.AbsoluteRight);
- end;
- msFaceCamera:
- begin
- c := Scene.CurrentCamera;
- if c <> nil then
- begin
- dv := VectorSubtract(fLastV0Pos,
- AffinevectorMake(obj.AbsolutePosition));
- v := VectorCrossProduct
- (AffinevectorMake(VectorSubtract(c.AbsolutePosition,
- obj.AbsolutePosition)), dv);
- NormalizeVector(v);
- end;
- end;
- else
- Assert(False, strErrorEx + strUnknownType);
- end;
- v0 := AffinevectorMake(obj.AbsolutePosition);
- VectorScale(v, width, v);
- p1 := VectorSubtract(v0, v);
- p2 := VectorAdd(v0, v);
- // PREVENT REFLAT
- if not PointIsInHalfSpace(p1, fLastV0Pos, VectorSubtract(v0, fLastV0Pos)) then
- p1 := fLastP1;
- if not PointIsInHalfSpace(p2, fLastV0Pos, VectorSubtract(v0, fLastV0Pos)) then
- p2 := fLastP2;
- if CreateMark(p1, p2, CurrentTime) then
- begin
- fLastV0Pos := v0;
- end;
- end;
- function TgxTrail.CreateMark(p1, p2: TVector3f; CurrentTime: Double): Boolean;
- var
- diff: integer;
- uv1, uv2: TTexpoint;
- apoint1, apoint2: TVector3f;
- currentvert: integer;
- i: integer;
- color: tVector4f;
- ramp: single;
- distance: single;
- uvsize: single;
- tinyoffset: TVector3f;
- MustRebuild: Boolean;
- begin
- Result := False;
- apoint1 := p1;
- apoint2 := p2;
- // get distance moved, based on average of 2 point movement;
- distance := (VectorDistance(fLastP1, p1) + VectorDistance(fLastP2, p2)) / 2;
- if distance = 0 then
- begin
- apoint1 := AffinevectorMake(fLastP1.X, fLastP1.Y, fLastP1.Z);
- apoint2 := AffinevectorMake(fLastP2.X, fLastP2.Y, fLastP2.Z);
- end;
- uvsize := distance / fUVScale; // scale UV's
- uv2.S := 0 + FLastUVs + uvsize;
- uv2.T := 0;
- uv1.S := 0 + FLastUVs + uvsize;
- uv1.T := 1;
- // process verts, then send them to .vertices for rendering
- if fVertEnd >= cMaxVerts then
- fVertEnd := 0;
- fVerts[fVertEnd + 1] := apoint2;
- fVerts[fVertEnd + 2] := apoint1;
- fUVs[fVertEnd + 1] := uv2;
- fUVs[fVertEnd + 2] := uv1;
- // tstamp := GetTickCount; // win api
- fTimeStamps[fVertEnd + 1] := CurrentTime;
- fTimeStamps[fVertEnd + 2] := CurrentTime;
- MustRebuild := False;
- if distance >= fMinDistance then
- begin
- inc(fVertCount, 2);
- inc(fVertEnd, 2);
- // remember stuff
- FLastUVs := FLastUVs + uvsize;
- fLastP1 := p1;
- fLastP2 := p2;
- MustRebuild := True;
- Result := True;
- end;
- // remove expired verts over VertLimit
- if fVertCount > fVertLimit then
- begin
- diff := fVertCount - fVertLimit;
- inc(fVertStart, diff);
- // inc start, reducing count to fit in limit - rollover handled later
- dec(fVertCount, diff);
- end;
- // remove time expired verts over TimeLimit
- // currentvert := fVertStart;
- for i := 0 to fVertCount - 1 do
- begin
- if (i + fVertStart) > cMaxVerts then
- currentvert := (i + fVertStart) - cMaxVerts // rollover
- else
- currentvert := (i + fVertStart);
- if fTimeLimit > 0 then
- if CurrentTime - fTimeStamps[currentvert] > fTimeLimit then
- begin
- inc(fVertStart, 1);
- // inc start, reducing count to fit in limit - rollover handled later
- dec(fVertCount, 1);
- MustRebuild := True;
- end;
- end;
- // handle rollover
- if fVertStart > cMaxVerts then
- fVertStart := 0 + (fVertStart - cMaxVerts); // adjust if rollover
- if MustRebuild then
- begin
- // give to .vertices, from start to count
- // currentvert := fVertStart;
- ramp := fAlpha / (fVertCount);
- color := Material.FrontProperties.Diffuse.color;
- vertices.Clear;
- for i := 0 to fVertCount - 1 do
- begin
- if (i + fVertStart) > cMaxVerts then
- currentvert := (i + fVertStart) - cMaxVerts // rollover
- else
- currentvert := (i + fVertStart);
- if fAlphaFade then
- color.W := (ramp * i)
- else
- color.W := fAlpha;
- // add a tiny bit of offset to help prevent z-fighting..
- // need a better solution here
- // as this will get out of whack on really long trails
- // and is dependant on scene scale
- tinyoffset.X := FAntiZFightOffset * i;
- tinyoffset.Y := FAntiZFightOffset * i;
- tinyoffset.Z := FAntiZFightOffset * i;
- tinyoffset := VectorAdd(fVerts[currentvert], tinyoffset);
- // TinyOffset := fVerts[ currentvert]; // bypass
- vertices.AddVertex(tinyoffset, NullVector, color, fUVs[currentvert]);
- end;
- end;
- end;
- procedure TgxTrail.CreateMark(APos, ADir, AUp: TVector3f; AWidth: single;
- ACurrentTime: Double);
- var
- apoint1, apoint2, crossp: TVector3f;
- begin
- if fMinDistance > 0 then
- if VectorDistance(APos, fLastPos) < fMinDistance then
- exit;
- fLastPos := APos;
- fLastDir := ADir;
- fLastUp := AUp;
- apoint1 := APos;
- apoint2 := APos;
- crossp := VectorCrossProduct(ADir, AUp);
- CombineVector(apoint1, vectornormalize(crossp), AWidth);
- CombineVector(apoint2, vectornormalize(VectorNegate(crossp)), AWidth);
- CreateMark(apoint1, apoint2, ACurrentTime);
- end;
- // NOTES and stuff:
- { // UV mapped 4x4 square for refrence /debug
- uv.S := 0; uv.T := 0;
- Vertices.AddVertex( AffineVectorMake(1, 1, 1), NullVector, NullHmgVector, UV );
- uv.S := 0; uv.T := 1;
- Vertices.AddVertex( AffineVectorMake(1, 1, 4), NullVector, NullHmgVector, UV );
- uv.S := 1; uv.T := 0;
- Vertices.AddVertex( AffineVectorMake(4, 1, 1), NullVector, NullHmgVector, UV );
- uv.S := 1; uv.T := 1;
- Vertices.AddVertex( AffineVectorMake(4, 1, 4), NullVector, NullHmgVector, UV );
- // Directmode: append .vertices only, no way to process/delete except .clear;
- // else we manage vertices/UV in our own arrays then dump them all to .vertices
- // I don't know if directmode is that much faster, but could be considerably?
- if directmode then
- begin
- if fUVTop then // start a new UV tile
- begin
- uv2.S := 0; uv2.T := 0;
- Vertices.AddVertex( AffineVectorMake(apoint2[0], apoint2[1],apoint2[2]), NullVector, NullHmgVector, UV2 );
- uv1.S := 0; uv1.T := 1;
- Vertices.AddVertex( AffineVectorMake(apoint1[0],apoint1[1],apoint1[2]), NullVector, NullHmgVector, UV1 );
- end
- else // finish a UV tile
- begin
- uv2.S := 1; uv2.T := 0;
- Vertices.AddVertex( AffineVectorMake(apoint2[0], apoint2[1],apoint2[2]), NullVector, NullHmgVector, UV2 );
- uv1.S := 1; uv1.T := 1;
- Vertices.AddVertex( AffineVectorMake(apoint1[0],apoint1[1],apoint1[2]), NullVector, NullHmgVector, UV1 );
- end;
- end
- }
- procedure TgxTrail.SetTrailObject(const Value: TgxBaseSceneObject);
- begin
- if FTrailObject <> nil then
- FTrailObject.RemoveFreeNotification(Self);
- FTrailObject := Value;
- if FTrailObject <> nil then
- FTrailObject.FreeNotification(Self);
- end;
- procedure TgxTrail.SetMarkStyle(const Value: TMarkStyle);
- begin
- FMarkStyle := Value;
- end;
- procedure TgxTrail.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- if (Operation = opRemove) and (AComponent = FTrailObject) then
- TrailObject := nil;
- inherited;
- end;
- procedure TgxTrail.SetAlpha(const Value: single);
- begin
- fAlpha := Value;
- end;
- procedure TgxTrail.SetAlphaFade(const Value: Boolean);
- begin
- fAlphaFade := Value;
- end;
- procedure TgxTrail.SetMinDistance(const Value: single);
- begin
- fMinDistance := Value;
- end;
- procedure TgxTrail.SetTimeLimit(const Value: single);
- begin
- fTimeLimit := Value;
- end;
- procedure TgxTrail.SetUVScale(const Value: single);
- begin
- fUVScale := Value;
- end;
- procedure TgxTrail.SetVertLimit(const Value: integer);
- begin
- fVertLimit := Value;
- end;
- procedure TgxTrail.SetMarkWidth(const Value: single);
- begin
- FMarkWidth := Value;
- end;
- procedure TgxTrail.SetEnabled(const Value: Boolean);
- begin
- FEnabled := Value;
- end;
- function TgxTrail.StoreAntiZFightOffset: Boolean;
- begin
- Result := FAntiZFightOffset <> 0.0000266;
- end;
- // ------------------------------------------------------------------
- initialization
- // ------------------------------------------------------------------
- RegisterClasses([TgxTrail]);
- end.
|