GXS.Trail.pas 13 KB

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