2
0

GLS.Trail.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Trail;
  5. (* Creates a trail-like mesh. *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. System.Classes,
  10. System.SysUtils,
  11. GLS.Scene,
  12. GLS.VectorTypes,
  13. GLS.MeshUtils,
  14. GLS.VectorGeometry,
  15. GLS.VectorFileObjects,
  16. GLS.Mesh,
  17. GLS.Objects,
  18. GLS.Material,
  19. GLS.Strings,
  20. GLS.BaseClasses;
  21. const cMaxVerts = 2000;
  22. type
  23. TMarkStyle = (msUp, msDirection, msFaceCamera, msRight);
  24. TGLTrail = class(TGlMesh)
  25. private
  26. fVertLimit: integer;
  27. fTimeLimit: single;
  28. fMinDistance: single;
  29. fAlpha: single;
  30. fAlphaFade: Boolean;
  31. fUVScale: Single;
  32. fVerts: array[1..cMaxVerts] of TVector3f;
  33. fUVs: array[1..cMaxVerts] of TTexpoint;
  34. fTimeStamps: array[1..cMaxVerts] of Double;
  35. fVertStart,fVertEnd,fVertCount: integer;
  36. fLastV0Pos,fLastPos, fLastDir, fLastUp: TVector3f;
  37. FLastUVs: single;
  38. // used for UV scaling
  39. fLastP1,fLastP2: TVector3f;
  40. FTrailObject: TGLBaseSceneObject;
  41. FMarkStyle: TMarkStyle;
  42. FMarkWidth: single;
  43. FEnabled: boolean;
  44. FAntiZFightOffset: Single;
  45. procedure SetTrailObject(const Value: TGLBaseSceneObject);
  46. procedure SetMarkStyle(const Value: TMarkStyle);
  47. procedure SetAlpha(const Value: single);
  48. procedure SetAlphaFade(const Value: Boolean);
  49. procedure SetMinDistance(const Value: single);
  50. procedure SetTimeLimit(const Value: single);
  51. procedure SetUVScale(const Value: single);
  52. procedure SetVertLimit(const Value: integer);
  53. procedure SetMarkWidth(const Value: single);
  54. procedure SetEnabled(const Value: boolean);
  55. function StoreAntiZFightOffset: Boolean;
  56. protected
  57. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  58. public
  59. //EnableUVmapping: boolean; // generate UV's or not
  60. procedure DoProgress(const progressTime : TGLProgressTimes); override;
  61. constructor Create(AOwner: TComponent); override;
  62. destructor Destroy; override;
  63. procedure CreateMark(obj: TGlBaseSceneObject; width: single;CurrentTime : Double); overload;
  64. procedure CreateMark(const APos,ADir,AUp: TVector3f; AWidth: single;ACurrentTime : Double); overload;
  65. function CreateMark(const p1,p2: TVector3f;CurrentTime : Double):boolean; overload;
  66. procedure ClearMarks;
  67. published
  68. {Add a tiny bit of offset to help prevent z-fighting..
  69. Need a better solution here as this will get out of whack on really
  70. long trails and is dependant on scene scale. }
  71. property AntiZFightOffset: Single read FAntiZFightOffset write FAntiZFightOffset stored StoreAntiZFightOffset;
  72. property VertLimit: integer read FVertLimit write SetVertLimit default 150;
  73. property TimeLimit: single read FTimeLimit write SetTimeLimit;
  74. {Don't create mark unless moved at least this distance. }
  75. property MinDistance: single read FMinDistance write SetMinDistance;
  76. property Alpha: single read FAlpha write SetAlpha;
  77. property AlphaFade: Boolean read FAlphaFade write SetAlphaFade default True;
  78. property UVScale: single read FUVScale write SetUVScale;
  79. property MarkStyle : TMarkStyle read FMarkStyle write SetMarkStyle default msFaceCamera;
  80. property TrailObject : TGLBaseSceneObject read FTrailObject write SetTrailObject default nil;
  81. property MarkWidth : single read FMarkWidth write SetMarkWidth;
  82. property Enabled : boolean read FEnabled write SetEnabled default True;
  83. end;
  84. implementation
  85. constructor TGLTrail.Create(AOwner: TComponent);
  86. begin
  87. inherited Create(AOwner);
  88. vertices.Clear; // inherited tglmesh makes a triangle... remove it.
  89. Mode := mmTriangleStrip;
  90. FAntiZFightOffset := 0.0000266;
  91. VertexMode := vmVNCT;
  92. fVertStart := 1;
  93. fVertEnd := 0;
  94. fVertCount := 0;
  95. fVertLimit := 150;
  96. fTimeLimit := 0.5;
  97. fMinDistance := 0.05;
  98. fAlphaFade := True;
  99. fAlpha := 1.0;
  100. fLastUVs := 0;
  101. fUVScale := 1.0;
  102. FMarkWidth := 0.5;
  103. FEnabled := True;
  104. FMarkStyle := msFaceCamera;
  105. Material.BlendingMode := bmAdditive;
  106. Material.FaceCulling := fcNoCull;
  107. end;
  108. destructor TGLTrail.Destroy;
  109. begin
  110. // notta?
  111. inherited Destroy;
  112. end;
  113. procedure TGLTrail.DoProgress(const progressTime: TGLProgressTimes);
  114. begin
  115. inherited;
  116. if Enabled and Assigned(TrailObject) then
  117. begin
  118. CreateMark(TrailObject,MarkWidth,progressTime.NewTime);
  119. end;
  120. end;
  121. procedure TGLTrail.ClearMarks;
  122. begin
  123. Vertices.Clear;
  124. fVertCount := 0;
  125. fVertEnd := 0;
  126. fVertStart := 1;
  127. end;
  128. procedure TGLTrail.CreateMark(obj: TGlBaseSceneObject; width: single;CurrentTime : Double);
  129. var
  130. v0,dv,p1,p2 : TVector3f;
  131. v: TVector3f;
  132. c: TGLCamera;
  133. begin
  134. case MarkStyle of
  135. msUp: begin
  136. v := AffinevectorMake(obj.AbsoluteUp);
  137. end;
  138. msDirection: begin
  139. v := AffinevectorMake(obj.AbsoluteDirection);
  140. end;
  141. msRight: begin
  142. v := AffinevectorMake(obj.AbsoluteRight);
  143. end;
  144. msFaceCamera: begin
  145. c := Scene.CurrentGLCamera;
  146. if c<>nil then
  147. begin
  148. dv := VectorSubtract(fLastV0Pos,AffinevectorMake(obj.AbsolutePosition));
  149. v := VectorCrossProduct(AffinevectorMake(VectorSubtract(c.AbsolutePosition,obj.AbsolutePosition)),dv);
  150. NormalizeVector(v);
  151. end;
  152. end;
  153. else Assert(False, strErrorEx + strUnknownType);
  154. end;
  155. v0 := AffinevectorMake(Obj.AbsolutePosition);
  156. VectorScale(v,width,v);
  157. p1:=VectorSubtract(v0,v);
  158. p2:=VectorAdd(v0,v);
  159. // PREVENT REFLAT
  160. if not PointIsInHalfSpace(p1,fLastV0Pos,VectorSubtract(v0,fLastV0Pos)) then
  161. p1 := fLastp1;
  162. if not PointIsInHalfSpace(p2,fLastV0Pos,VectorSubtract(v0,fLastV0Pos)) then
  163. p2 := fLastp2;
  164. if CreateMark(p1,p2,CurrentTime) then
  165. begin
  166. fLastV0Pos := v0;
  167. end;
  168. end;
  169. function TGLTrail.CreateMark(const p1,p2: TVector3f; CurrentTime : Double): boolean;
  170. var
  171. diff: integer;
  172. uv1,uv2: TTexPoint;
  173. apoint1,apoint2: TVector3f;
  174. currentvert: integer;
  175. i: integer;
  176. color: tVector4f;
  177. ramp: single;
  178. distance: single;
  179. uvsize: single;
  180. tinyoffset: TVector3f;
  181. MustRebuild : Boolean;
  182. begin
  183. Result := false;
  184. apoint1 := p1;
  185. apoint2 := p2;
  186. // get distance moved, based on average of 2 point movement;
  187. distance := ( VectorDistance(fLastp1,p1) + VectorDistance(fLastp2,p2) ) / 2;
  188. if distance = 0 then
  189. begin
  190. apoint1 := AffineVectorMake(fLastp1.X,fLastp1.Y,fLastp1.Z);
  191. apoint2 := AffineVectorMake(fLastp2.X,fLastp2.Y,fLastp2.Z);
  192. end;
  193. uvsize := distance / fUVScale; // scale UV's
  194. uv2.S := 0+fLastUVs+uvsize; uv2.T := 0;
  195. uv1.S := 0+fLastUVs+uvsize; uv1.T := 1;
  196. // process verts, then send them to .vertices for rendering
  197. if fVertEnd >= cMaxVerts then
  198. fVertEnd := 0;
  199. fVerts[fVertEnd+1] := apoint2;
  200. fVerts[fVertEnd+2] := apoint1;
  201. fUVs[fVertEnd+1] := uv2;
  202. fUVs[fVertEnd+2] := uv1;
  203. //tstamp := GetTickCount; // win api
  204. fTimeStamps[fVertEnd+1] := CurrentTime;
  205. fTimeStamps[fVertEnd+2] := CurrentTime;
  206. MustRebuild := false;
  207. if distance >= fMinDistance then
  208. begin
  209. inc(fVertCount,2);
  210. inc(fVertEnd,2);
  211. // remember stuff
  212. fLastUVs := fLastUVs + uvsize;
  213. fLastp1 := p1; fLastp2 := p2;
  214. MustRebuild := true;
  215. Result := true;
  216. end;
  217. // remove expired verts over VertLimit
  218. if fVertCount > fVertLimit then
  219. begin
  220. diff := fVertCount - fVertLimit;
  221. inc(fVertStart,diff); // inc start, reducing count to fit in limit - rollover handled later
  222. dec(fVertCount,diff);
  223. end;
  224. // remove time expired verts over TimeLimit
  225. //currentvert := fVertStart;
  226. for i := 0 to fVertCount-1 do
  227. begin
  228. if (i + fVertStart) > cMaxVerts then
  229. currentvert := (i + fVertStart) - cMaxVerts // rollover
  230. else
  231. currentvert := (i + fVertStart);
  232. if fTimeLimit > 0 then
  233. if CurrentTime - fTimeStamps[ currentvert ] > fTimeLimit then
  234. begin
  235. inc(fVertStart,1); // inc start, reducing count to fit in limit - rollover handled later
  236. dec(fVertCount,1);
  237. MustRebuild := true;
  238. end;
  239. end;
  240. // handle rollover
  241. if fVertStart > cMaxVerts then fVertStart := 0 + ( fVertStart - cMaxVerts); // adjust if rollover
  242. if MustRebuild then
  243. begin
  244. // give to .vertices, from start to count
  245. //currentvert := fVertStart;
  246. ramp := fAlpha / (fVertCount) ;
  247. color := material.FrontProperties.Diffuse.Color;
  248. Vertices.Clear;
  249. for i := 0 to fVertCount-1 do
  250. begin
  251. if (i + fVertStart) > cMaxVerts then
  252. currentvert := (i + fVertStart) - cMaxVerts // rollover
  253. else
  254. currentvert := (i + fVertStart);
  255. if fAlphaFade then
  256. color.W := (ramp * i)
  257. else
  258. color.W := fAlpha;
  259. // add a tiny bit of offset to help prevent z-fighting..
  260. // need a better solution here
  261. // as this will get out of whack on really long trails
  262. // and is dependant on scene scale
  263. TinyOffset.X := FAntiZFightOffset * i;
  264. TinyOffset.Y := FAntiZFightOffset * i;
  265. TinyOffset.Z := FAntiZFightOffset * i;
  266. TinyOffset := VectorAdd( fVerts[ currentvert ],Tinyoffset);
  267. //TinyOffset := fVerts[ currentvert]; // bypass
  268. Vertices.AddVertex( TinyOffset, NullVector, Color, fUVs[currentvert] );
  269. end;
  270. end;
  271. end;
  272. procedure TGLTrail.CreateMark(const APos,ADir,AUp: TVector3f; AWidth: single;ACurrentTime : Double);
  273. var
  274. apoint1,apoint2,crossp: TVector3f;
  275. begin
  276. if fMinDistance > 0 then
  277. if vectorDistance(APos,fLastPos) < fMinDistance then
  278. exit;
  279. fLastPos := APos;
  280. fLastDir := ADir;
  281. fLastUp := AUp;
  282. apoint1 := APos;
  283. apoint2 := APos;
  284. crossp := vectorcrossproduct(ADir,AUp);
  285. CombineVector( apoint1,vectornormalize(crossp),AWidth);
  286. CombineVector( apoint2,vectornormalize(VectorNegate(crossp)),AWidth);
  287. CreateMark( apoint1, apoint2,ACurrentTime);
  288. end;
  289. // NOTES and stuff:
  290. { // UV mapped 4x4 square for refrence /debug
  291. uv.S := 0; uv.T := 0;
  292. Vertices.AddVertex( AffineVectorMake(1, 1, 1), NullVector, NullHmgVector, UV );
  293. uv.S := 0; uv.T := 1;
  294. Vertices.AddVertex( AffineVectorMake(1, 1, 4), NullVector, NullHmgVector, UV );
  295. uv.S := 1; uv.T := 0;
  296. Vertices.AddVertex( AffineVectorMake(4, 1, 1), NullVector, NullHmgVector, UV );
  297. uv.S := 1; uv.T := 1;
  298. Vertices.AddVertex( AffineVectorMake(4, 1, 4), NullVector, NullHmgVector, UV );
  299. // Directmode: append .vertices only, no way to process/delete except .clear;
  300. // else we manage vertices/UV in our own arrays then dump them all to .vertices
  301. // I don't know if directmode is that much faster, but could be considerably?
  302. if directmode then
  303. begin
  304. if fUVTop then // start a new UV tile
  305. begin
  306. uv2.S := 0; uv2.T := 0;
  307. Vertices.AddVertex( AffineVectorMake(apoint2[0], apoint2[1],apoint2[2]), NullVector, NullHmgVector, UV2 );
  308. uv1.S := 0; uv1.T := 1;
  309. Vertices.AddVertex( AffineVectorMake(apoint1[0],apoint1[1],apoint1[2]), NullVector, NullHmgVector, UV1 );
  310. end
  311. else // finish a UV tile
  312. begin
  313. uv2.S := 1; uv2.T := 0;
  314. Vertices.AddVertex( AffineVectorMake(apoint2[0], apoint2[1],apoint2[2]), NullVector, NullHmgVector, UV2 );
  315. uv1.S := 1; uv1.T := 1;
  316. Vertices.AddVertex( AffineVectorMake(apoint1[0],apoint1[1],apoint1[2]), NullVector, NullHmgVector, UV1 );
  317. end;
  318. end
  319. }
  320. procedure TGLTrail.SetTrailObject(const Value: TGLBaseSceneObject);
  321. begin
  322. if FTrailObject <> nil then FTrailObject.RemoveFreeNotification(Self);
  323. FTrailObject := Value;
  324. if FTrailObject <> nil then FTrailObject.FreeNotification(Self);
  325. end;
  326. procedure TGLTrail.SetMarkStyle(const Value: TMarkStyle);
  327. begin
  328. FMarkStyle := Value;
  329. end;
  330. procedure TGLTrail.Notification(AComponent: TComponent;
  331. Operation: TOperation);
  332. begin
  333. if (Operation = opRemove) and (AComponent = FTrailObject) then
  334. TrailObject:=nil;
  335. inherited;
  336. end;
  337. procedure TGLTrail.SetAlpha(const Value: single);
  338. begin
  339. FAlpha := Value;
  340. end;
  341. procedure TGLTrail.SetAlphaFade(const Value: Boolean);
  342. begin
  343. FAlphaFade := Value;
  344. end;
  345. procedure TGLTrail.SetMinDistance(const Value: single);
  346. begin
  347. FMinDistance := Value;
  348. end;
  349. procedure TGLTrail.SetTimeLimit(const Value: single);
  350. begin
  351. FTimeLimit := Value;
  352. end;
  353. procedure TGLTrail.SetUVScale(const Value: single);
  354. begin
  355. FUVScale := Value;
  356. end;
  357. procedure TGLTrail.SetVertLimit(const Value: integer);
  358. begin
  359. FVertLimit := Value;
  360. end;
  361. procedure TGLTrail.SetMarkWidth(const Value: single);
  362. begin
  363. FMarkWidth := Value;
  364. end;
  365. procedure TGLTrail.SetEnabled(const Value: boolean);
  366. begin
  367. FEnabled := Value;
  368. end;
  369. function TGLTrail.StoreAntiZFightOffset: Boolean;
  370. begin
  371. Result := FAntiZFightOffset <> 0.0000266;
  372. end;
  373. // ------------------------------------------------------------------
  374. // ------------------------------------------------------------------
  375. // ------------------------------------------------------------------
  376. initialization
  377. // ------------------------------------------------------------------
  378. // ------------------------------------------------------------------
  379. // ------------------------------------------------------------------
  380. // class registrations
  381. RegisterClasses([TGLTrail]);
  382. end.