GXS.FPSMovement.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.FPSMovement;
  5. (* FPS-like movement behaviour and manager *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.SysUtils,
  12. System.UITypes,
  13. FMX.Graphics,
  14. Stage.VectorTypes,
  15. Stage.VectorGeometry,
  16. Stage.Manager,
  17. GXS.Context,
  18. GXS.Scene,
  19. GXS.Coordinates,
  20. GXS.VectorFileObjects,
  21. GXS.VectorLists,
  22. GXS.GeomObjects,
  23. GXS.Navigator,
  24. GXS.RenderContextInfo,
  25. GXS.BaseClasses,
  26. GXS.XCollection,
  27. GXS.State;
  28. type
  29. TContactPoint = record
  30. intPoint, intNormal: TVector4f;
  31. end;
  32. TCollisionState = class
  33. public
  34. Position: TVector4f;
  35. Contact: TContactPoint;
  36. Time: Int64;
  37. end;
  38. TCollisionStates = class(TList)
  39. end;
  40. TgxBFPSMovement = class;
  41. TgxMapCollectionItem = class(TXCollectionItem)
  42. private
  43. FMap: TgxFreeForm;
  44. FMapName: string;
  45. FCollisionGroup: integer;
  46. procedure setMap(value: TgxFreeForm);
  47. protected
  48. procedure WriteToFiler(writer: TWriter); override;
  49. procedure ReadFromFiler(reader: TReader); override;
  50. procedure Loaded; override;
  51. public
  52. constructor Create(aOwner: TXCollection); override;
  53. class function FriendlyName: String; override;
  54. published
  55. property Map: TgxFreeForm read FMap write setMap;
  56. (* Indicates the collision group of this map. A Collision Group
  57. is a set of logical maps and movers that can collide between
  58. themselves (i.e. a Behaviour with group 1 can only collide with
  59. maps that are also on group 1). *)
  60. property CollisionGroup: integer read FCollisionGroup write FCollisionGroup;
  61. end;
  62. TgxMapCollectionItemClass = class of TgxMapCollectionItem;
  63. TgxMapCollection = class(TXCollection)
  64. public
  65. class function ItemsClass: TXCollectionItemClass; override;
  66. function addMap(Map: TgxFreeForm; CollisionGroup: integer = 0)
  67. : TgxMapCollectionItem;
  68. function findMap(mapFreeForm: TgxFreeForm): TgxMapCollectionItem;
  69. end;
  70. TgxFPSMovementManager = class(TComponent)
  71. private
  72. FNavigator: TgxNavigator;
  73. FDisplayTime: integer;
  74. FMovementScale: single;
  75. FMaps: TgxMapCollection;
  76. FScene: TgxScene;
  77. procedure SetNavigator(value: TgxNavigator);
  78. procedure setScene(value: TgxScene);
  79. procedure DrawArrows(intPoint, intNormal, Ray: TVector4f;
  80. Arrow1, Arrow2: TgxArrowLine);
  81. protected
  82. procedure Loaded; override;
  83. procedure DefineProperties(Filer: TFiler); override;
  84. procedure WriteMaps(stream: TStream);
  85. procedure ReadMaps(stream: TStream);
  86. procedure Notification(AComponent: TComponent;
  87. Operation: TOperation); override;
  88. public
  89. constructor Create(aOwner: TComponent); override;
  90. destructor Destroy; override;
  91. (* Basic idea is to OctreeSphereSweepIntersect to plane, update position then change
  92. velocity to slide along the plane
  93. Camera can collide with multiple planes (e.g. floor + multiple walls + ceiling)
  94. limit iterations to 4 or 5 for now, may need to be higher
  95. for more complex maps or fast motion *)
  96. function SphereSweepAndSlide(freeform: TgxFreeForm;
  97. behaviour: TgxBFPSMovement; SphereStart: TVector4f;
  98. var Velocity, newPosition: TVector4f; sphereRadius: single)
  99. : boolean; overload;
  100. procedure SphereSweepAndSlide(behaviour: TgxBFPSMovement;
  101. SphereStart: TVector4f; var Velocity, newPosition: TVector4f;
  102. sphereRadius: single); overload;
  103. published
  104. property Maps: TgxMapCollection read FMaps write FMaps;
  105. property Navigator: TgxNavigator read FNavigator write SetNavigator;
  106. property Scene: TgxScene read FScene write setScene;
  107. // Display Time for the arrow lines.
  108. property DisplayTime: integer read FDisplayTime write FDisplayTime;
  109. property MovementScale: single read FMovementScale write FMovementScale;
  110. end;
  111. TgxBFPSMovement = class(TgxBehaviour)
  112. private
  113. FManager: TgxFPSMovementManager;
  114. CollisionStates: TCollisionStates;
  115. ArrowLine1, ArrowLine2, ArrowLine3, ArrowLine4, ArrowLine5,
  116. ArrowLine6: TgxArrowLine;
  117. dirGl: TgxDirectOpenGL;
  118. tickCount: Int64;
  119. oldPosition: TVector4f;
  120. FGravityEnabled: boolean;
  121. FSphereRadius: single;
  122. FShowArrows: boolean;
  123. FCollisionGroup: integer;
  124. FManagerName: string;
  125. procedure setShowArrows(value: boolean);
  126. procedure RenderArrowLines(Sender: TObject; var rci: TgxRenderContextInfo);
  127. protected
  128. procedure WriteToFiler(writer: TWriter); override;
  129. procedure ReadFromFiler(reader: TReader); override;
  130. procedure Loaded; override;
  131. public
  132. Velocity: TVector4f;
  133. constructor Create(aOwner: TXCollection); override;
  134. destructor Destroy; override;
  135. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  136. class function FriendlyName: string; override;
  137. Procedure TurnHorizontal(Angle: single);
  138. Procedure TurnVertical(Angle: single);
  139. Procedure MoveForward(Distance: single);
  140. Procedure StrafeHorizontal(Distance: single);
  141. Procedure StrafeVertical(Distance: single);
  142. Procedure Straighten;
  143. published
  144. property Manager: TgxFPSMovementManager read FManager write FManager;
  145. (*
  146. Radius to execute the testing with. A value < 0 indicates to use
  147. the boundingSphereRadius of the object.
  148. *)
  149. property sphereRadius: single read FSphereRadius write FSphereRadius;
  150. // Show Arrows and trailing for debuging.
  151. property ShowArrows: boolean read FShowArrows write setShowArrows;
  152. (* Indicates the collision group of this behaviour. A Collision Group
  153. is a set of logical maps and movers that can collide between
  154. themselves (i.e. a Behaviour with group 1 can only collide with
  155. maps that are also on group 1) *)
  156. property CollisionGroup: integer read FCollisionGroup write FCollisionGroup;
  157. property GravityEnabled: boolean read FGravityEnabled write FGravityEnabled;
  158. end;
  159. function GetFPSMovement(behaviours: TgxBehaviours): TgxBFPSMovement; overload;
  160. function GetFPSMovement(obj: TgxBaseSceneObject): TgxBFPSMovement; overload;
  161. function GetOrCreateFPSMovement(behaviours: TgxBehaviours): TgxBFPSMovement; overload;
  162. function GetOrCreateFPSMovement(obj: TgxBaseSceneObject): TgxBFPSMovement; overload;
  163. //-------------------------------------------------------------------------
  164. implementation
  165. //-------------------------------------------------------------------------
  166. function GetFPSMovement(behaviours: TgxBehaviours): TgxBFPSMovement; overload;
  167. var
  168. i: integer;
  169. begin
  170. i := behaviours.IndexOfClass(TgxBFPSMovement);
  171. if i >= 0 then
  172. Result := TgxBFPSMovement(behaviours[i])
  173. else
  174. Result := nil;
  175. end;
  176. function GetFPSMovement(obj: TgxBaseSceneObject): TgxBFPSMovement; overload;
  177. begin
  178. Result := GetFPSMovement(obj.behaviours);
  179. end;
  180. function GetOrCreateFPSMovement(behaviours: TgxBehaviours)
  181. : TgxBFPSMovement; overload;
  182. var
  183. i: integer;
  184. begin
  185. i := behaviours.IndexOfClass(TgxBFPSMovement);
  186. if i >= 0 then
  187. Result := TgxBFPSMovement(behaviours[i])
  188. else
  189. Result := TgxBFPSMovement.Create(behaviours);
  190. end;
  191. function GetOrCreateFPSMovement(obj: TgxBaseSceneObject)
  192. : TgxBFPSMovement; overload;
  193. begin
  194. Result := GetOrCreateFPSMovement(obj.behaviours);
  195. end;
  196. // ------------------
  197. // ------------------ TgxMapCollectionItem ------------------
  198. // ------------------
  199. constructor TgxMapCollectionItem.Create(aOwner: TXCollection);
  200. begin
  201. inherited Create(aOwner);
  202. FCollisionGroup := 0;
  203. end;
  204. procedure TgxMapCollectionItem.setMap(value: TgxFreeForm);
  205. begin
  206. assert(owner.owner.InheritsFrom(TgxFPSMovementManager));
  207. if assigned(FMap) then
  208. FMap.RemoveFreeNotification(TComponent(owner.owner));
  209. FMap := value;
  210. if assigned(FMap) then
  211. FMap.FreeNotification(TComponent(owner.owner));
  212. end;
  213. procedure TgxMapCollectionItem.WriteToFiler(writer: TWriter);
  214. begin
  215. inherited WriteToFiler(writer);
  216. with writer do
  217. begin
  218. writeInteger(0); // ArchiveVersion
  219. writeInteger(FCollisionGroup);
  220. if assigned(FMap) then
  221. WriteString(FMap.Name)
  222. else
  223. WriteString('');
  224. end;
  225. end;
  226. procedure TgxMapCollectionItem.ReadFromFiler(reader: TReader);
  227. var
  228. archiveVersion: integer;
  229. begin
  230. inherited ReadFromFiler(reader);
  231. with reader do
  232. begin
  233. archiveVersion := readInteger;
  234. assert(archiveVersion = 0, 'Wrong ArchiveVersion for TgxMapCollectionItem');
  235. FCollisionGroup := readInteger;
  236. FMapName := ReadString;
  237. end;
  238. end;
  239. procedure TgxMapCollectionItem.Loaded;
  240. begin
  241. if FMapName <> '' then
  242. begin
  243. assert(owner.owner.InheritsFrom(TgxFPSMovementManager));
  244. Map := TgxFreeForm(TgxFPSMovementManager(owner.owner)
  245. .Scene.FindSceneObject(FMapName));
  246. end;
  247. end;
  248. class function TgxMapCollectionItem.FriendlyName: String;
  249. begin
  250. Result := 'FPSMovementMap';
  251. end;
  252. // ------------------
  253. // ------------------ TgxMapCollection ------------------
  254. // ------------------
  255. class function TgxMapCollection.ItemsClass: TXCollectionItemClass;
  256. begin
  257. Result := TgxMapCollectionItem;
  258. end;
  259. function TgxMapCollection.addMap(Map: TgxFreeForm; CollisionGroup: integer = 0)
  260. : TgxMapCollectionItem;
  261. begin
  262. // no repeated maps (would only present delays...)
  263. Result := findMap(Map);
  264. if assigned(Result) then
  265. exit;
  266. Result := TgxMapCollectionItem.Create(self);
  267. Result.Map := Map;
  268. Result.CollisionGroup := CollisionGroup;
  269. add(Result);
  270. end;
  271. function TgxMapCollection.findMap(mapFreeForm: TgxFreeForm)
  272. : TgxMapCollectionItem;
  273. var
  274. i: integer;
  275. aux: TgxMapCollectionItem;
  276. begin
  277. Result := nil;
  278. for i := 0 to count - 1 do
  279. begin
  280. aux := TgxMapCollectionItem(Items[i]);
  281. if aux.Map = mapFreeForm then
  282. begin
  283. Result := aux;
  284. break;
  285. end;
  286. end;
  287. end;
  288. // ------------------
  289. // ------------------ TgxFPSMovementManager ------------------
  290. // ------------------
  291. constructor TgxFPSMovementManager.Create(aOwner: TComponent);
  292. begin
  293. inherited Create(aOwner);
  294. Maps := TgxMapCollection.Create(self);
  295. MovementScale := 4.0;
  296. DisplayTime := 2000;
  297. RegisterManager(self);
  298. end;
  299. destructor TgxFPSMovementManager.Destroy;
  300. begin
  301. DeRegisterManager(self);
  302. Maps.Free;
  303. inherited Destroy;
  304. end;
  305. procedure TgxFPSMovementManager.Loaded;
  306. begin
  307. inherited Loaded;
  308. if assigned(FMaps) then
  309. Maps.Loaded;
  310. end;
  311. // DefineProperties
  312. //
  313. procedure TgxFPSMovementManager.DefineProperties(Filer: TFiler);
  314. begin
  315. inherited;
  316. { FOriginalFiler := Filer; }
  317. Filer.DefineBinaryProperty('MapsData', ReadMaps, WriteMaps,
  318. (assigned(FMaps) and (FMaps.count > 0)));
  319. { FOriginalFiler:=nil; }
  320. end;
  321. // WriteBehaviours
  322. //
  323. procedure TgxFPSMovementManager.WriteMaps(stream: TStream);
  324. var
  325. writer: TWriter;
  326. begin
  327. writer := TWriter.Create(stream, 16384);
  328. try
  329. Maps.WriteToFiler(writer);
  330. finally
  331. writer.Free;
  332. end;
  333. end;
  334. procedure TgxFPSMovementManager.ReadMaps(stream: TStream);
  335. var
  336. reader: TReader;
  337. begin
  338. reader := TReader.Create(stream, 16384);
  339. try
  340. Maps.ReadFromFiler(reader);
  341. finally
  342. reader.Free;
  343. end;
  344. end;
  345. procedure TgxFPSMovementManager.SetNavigator(value: TgxNavigator);
  346. begin
  347. if assigned(FNavigator) then
  348. FNavigator.RemoveFreeNotification(self);
  349. FNavigator := value;
  350. if assigned(value) then
  351. value.FreeNotification(self);
  352. end;
  353. procedure TgxFPSMovementManager.setScene(value: TgxScene);
  354. begin
  355. if assigned(FScene) then
  356. FScene.RemoveFreeNotification(self);
  357. FScene := value;
  358. if assigned(FScene) then
  359. FScene.FreeNotification(self);
  360. end;
  361. procedure TgxFPSMovementManager.Notification(AComponent: TComponent;
  362. Operation: TOperation);
  363. var
  364. Map: TgxMapCollectionItem;
  365. begin
  366. inherited Notification(AComponent, Operation);
  367. if Operation <> opRemove then
  368. exit;
  369. if (AComponent = FNavigator) then
  370. Navigator := nil;
  371. if (AComponent = FScene) then
  372. FScene := nil;
  373. if AComponent.InheritsFrom(TgxFreeForm) then
  374. begin
  375. Map := Maps.findMap(TgxFreeForm(AComponent));
  376. if assigned(Map) then
  377. Map.Map := nil;
  378. end;
  379. end;
  380. procedure TgxFPSMovementManager.DrawArrows(intPoint, intNormal, Ray: TVector4f;
  381. Arrow1, Arrow2: TgxArrowLine);
  382. begin
  383. Arrow1.Position.AsVector := intPoint;
  384. Arrow1.Direction.AsVector := intNormal;
  385. Arrow1.Scale.z := VectorLength(intNormal);
  386. Arrow1.Move(Arrow1.Scale.z / 2);
  387. Arrow1.Visible := True;
  388. Arrow2.Position.AsVector := intPoint;
  389. Arrow2.Direction.AsVector := Ray;
  390. Arrow2.Visible := True;
  391. end;
  392. procedure TgxFPSMovementManager.SphereSweepAndSlide(behaviour: TgxBFPSMovement;
  393. SphereStart: TVector4f; var Velocity, newPosition: TVector4f;
  394. sphereRadius: single);
  395. var
  396. i: integer;
  397. Map: TgxMapCollectionItem;
  398. begin
  399. for i := 0 to Maps.count - 1 do
  400. begin
  401. Map := TgxMapCollectionItem(Maps.GetItems(i));
  402. if Map.CollisionGroup = behaviour.CollisionGroup then
  403. SphereSweepAndSlide(Map.Map, behaviour, SphereStart, Velocity,
  404. newPosition, sphereRadius)
  405. end;
  406. end;
  407. function TgxFPSMovementManager.SphereSweepAndSlide(freeform: TgxFreeForm;
  408. behaviour: TgxBFPSMovement; SphereStart: TVector4f;
  409. var Velocity, newPosition: TVector4f; sphereRadius: single): boolean;
  410. var
  411. oldPosition, Ray: TVector4f;
  412. vel, slidedistance: single;
  413. intPoint, intNormal: TVector4f;
  414. newDirection, newRay, collisionPosition, pointOnSphere,
  415. point2OnSphere: TVector4f;
  416. i: integer;
  417. CollisionState: TCollisionState;
  418. SphereRadiusRel: single; // mrqzzz
  419. begin
  420. SphereRadiusRel := sphereRadius / freeform.Scale.x;
  421. // could be Scale.y, or Scale.z assuming they are the same
  422. oldPosition := SphereStart;
  423. Result := True;
  424. // Direction sphere is moving in
  425. Ray := VectorSubtract(newPosition, oldPosition);
  426. // ray:=Velocity;
  427. // newPosition:=VectorAdd(newPosition,ray);
  428. // Speed of sphere
  429. vel := VectorLength(Ray);
  430. // if the Sphere is not moving, nothing is required
  431. // else do up to 7 loops
  432. if vel > 0 then
  433. for i := 0 to 6 do
  434. begin
  435. // if an intersection occurs, will need to do further calculations
  436. if (freeform.OctreeSphereSweepIntersect(oldPosition, Ray, vel,
  437. SphereRadiusRel, @intPoint, @intNormal)) then
  438. begin
  439. if VectorDistance2(oldPosition, intPoint) <= sqr(sphereRadius) then
  440. begin
  441. // sphere is intersecting triangle
  442. intNormal := VectorScale(VectorSubtract(oldPosition,
  443. intPoint), 1.0001);
  444. end
  445. else
  446. begin
  447. // sphere is not intersecting triangle
  448. // intNormal:=VectorSubtract(oldPosition,intPoint); //not correct but works okay at small time steps
  449. // intNormal:=VectorScale(VectorNormalize(intNormal),SphereRadius+0.0001);
  450. if RayCastSphereInterSect(intPoint, VectorNormalize(VectorNegate(Ray)
  451. ), oldPosition, sphereRadius, pointOnSphere, point2OnSphere) > 0
  452. then
  453. intNormal := VectorScale(VectorSubtract(oldPosition,
  454. pointOnSphere), 1.0001)
  455. // intNormal:=VectorScale(VectorNormalize(VectorSubtract(oldPosition,PointOnSphere)),SphereRadius+0.001)//VectorDistance(oldPosition,PointOnSphere));
  456. else
  457. begin
  458. // Assert(False); //this shouldn't happen (this is here for debugging)
  459. intNormal := VectorScale(VectorSubtract(oldPosition,
  460. intPoint), 1.0001);
  461. end;
  462. end;
  463. // calculate position of centre of sphere when collision occurs
  464. collisionPosition := VectorAdd(intPoint, intNormal);
  465. oldPosition := collisionPosition;
  466. // calculate distance that wasn't travelled, due to obstacle
  467. newRay := VectorSubtract(newPosition, collisionPosition);
  468. // calculate new direction when a wall is hit (could add bouncing to this)
  469. newDirection := VectorCrossProduct(intNormal,
  470. VectorCrossProduct(newRay, intNormal));
  471. if VectorNorm(newDirection) > 0 then
  472. NormalizeVector(newDirection);
  473. // calculate distance that it should slide (depends on angle between plane & ray)
  474. slidedistance := vectorDotProduct(newRay, newDirection);
  475. // still need to implement friction properly
  476. // if abs(SlideDistance)<10*deltaTime then SlideDistance:=0;
  477. ScaleVector(newDirection, slidedistance);
  478. // calculate new position sphere is heading towards
  479. newPosition := VectorAdd(collisionPosition, newDirection);
  480. Ray := newDirection;
  481. vel := VectorLength(Ray);
  482. // display arrows for collision normals & slide direction
  483. if (i = 0) and (behaviour.ShowArrows) then
  484. DrawArrows(intPoint, intNormal, Ray, behaviour.ArrowLine1,
  485. behaviour.ArrowLine4)
  486. else if (i = 1) and (behaviour.ShowArrows) then
  487. DrawArrows(intPoint, intNormal, Ray, behaviour.ArrowLine2,
  488. behaviour.ArrowLine5)
  489. else if (i = 2) and (behaviour.ShowArrows) then
  490. DrawArrows(intPoint, intNormal, Ray, behaviour.ArrowLine3,
  491. behaviour.ArrowLine6)
  492. else if i = 6 then
  493. begin
  494. // caption:=FloatToStr(vectordistance(newPosition,oldPosition));
  495. newPosition := oldPosition;
  496. break;
  497. end;
  498. // check if very small motion (e.g. when stuck in a corner)
  499. if vel < 1E-10 then // deltaTime then
  500. begin
  501. newPosition := oldPosition;
  502. break;
  503. end;
  504. CollisionState := TCollisionState.Create();
  505. CollisionState.Position := oldPosition;
  506. CollisionState.Contact.intNormal := intNormal;
  507. CollisionState.Contact.intPoint := intPoint;
  508. CollisionState.Time := TThread.GetTickCount();
  509. behaviour.CollisionStates.add(CollisionState);
  510. end
  511. else // no collision occured, so quit loop
  512. begin
  513. if i = 0 then
  514. Result := false;
  515. break;
  516. end;
  517. end; // end i loop
  518. Velocity := Ray;
  519. end;
  520. // ------------------
  521. // ------------------ TgxBFPSMovement ------------------
  522. // ------------------
  523. constructor TgxBFPSMovement.Create(aOwner: TXCollection);
  524. procedure setupArrow(arrow: TgxArrowLine; color: TColor);
  525. begin
  526. with arrow do
  527. begin
  528. slices := 16;
  529. stacks := 4;
  530. TopArrowHeadHeight := 0.1;
  531. TopArrowHeadRadius := 0.04;
  532. TopRadius := 0.02;
  533. BottomArrowHeadHeight := 0.05;
  534. BottomArrowHeadRadius := 0.02;
  535. BottomRadius := 0.02;
  536. Material.FrontProperties.Diffuse.AsWinColor := color;
  537. end;
  538. end;
  539. begin
  540. inherited Create(aOwner);
  541. Velocity := NullHmgVector;
  542. sphereRadius := -1;
  543. CollisionGroup := 0;
  544. CollisionStates := TCollisionStates.Create;
  545. // FIXME: Creating arrows here, but they should be only added when
  546. // a "showArrows" property changed
  547. ArrowLine1 := TgxArrowLine.Create(nil);
  548. setupArrow(ArrowLine1, TColors.Red);
  549. ArrowLine2 := TgxArrowLine.Create(nil);
  550. setupArrow(ArrowLine2, TColors.Green);
  551. ArrowLine3 := TgxArrowLine.Create(nil);
  552. setupArrow(ArrowLine3, TColors.Blue);
  553. ArrowLine4 := TgxArrowLine.Create(nil);
  554. setupArrow(ArrowLine4, TColors.Silver);
  555. ArrowLine5 := TgxArrowLine.Create(nil);
  556. setupArrow(ArrowLine5, TColors.Silver);
  557. ArrowLine6 := TgxArrowLine.Create(nil);
  558. setupArrow(ArrowLine6, TColors.Silver);
  559. dirGl := TgxDirectOpenGL.Create(nil);
  560. dirGl.OnRender := RenderArrowLines;
  561. oldPosition := OwnerBaseSceneObject.Position.AsVector;
  562. FManagerName := '';
  563. end;
  564. destructor TgxBFPSMovement.Destroy;
  565. var
  566. i: integer;
  567. begin
  568. // remove all states
  569. for i := 0 to CollisionStates.count - 1 do
  570. TCollisionState(CollisionStates[i]).Free;
  571. FreeAndNil(CollisionStates);
  572. // remove all objects used to display graphical results of collisions
  573. FreeAndNil(ArrowLine1);
  574. FreeAndNil(ArrowLine2);
  575. FreeAndNil(ArrowLine3);
  576. FreeAndNil(ArrowLine4);
  577. FreeAndNil(ArrowLine5);
  578. FreeAndNil(ArrowLine6);
  579. FreeAndNil(dirGl);
  580. inherited Destroy;
  581. end;
  582. class function TgxBFPSMovement.FriendlyName: String;
  583. begin
  584. Result := 'FPS Movement';
  585. end;
  586. procedure TgxBFPSMovement.WriteToFiler(writer: TWriter);
  587. begin
  588. inherited WriteToFiler(writer);
  589. with writer do
  590. begin
  591. writeInteger(0); // ArchiveVersion 0 (initial)
  592. writeInteger(FCollisionGroup);
  593. WriteSingle(FSphereRadius);
  594. WriteBoolean(FGravityEnabled);
  595. WriteBoolean(FShowArrows);
  596. if assigned(FManager) then
  597. WriteString(FManager.GetNamePath)
  598. else
  599. WriteString('');
  600. end;
  601. end;
  602. procedure TgxBFPSMovement.ReadFromFiler(reader: TReader);
  603. var
  604. archiveVersion: integer;
  605. begin
  606. inherited ReadFromFiler(reader);
  607. with reader do
  608. begin
  609. archiveVersion := readInteger;
  610. assert(archiveVersion = 0, 'Wrong ArchiveVersion for TgxBFPSMovement');
  611. CollisionGroup := readInteger;
  612. sphereRadius := ReadSingle;
  613. GravityEnabled := ReadBoolean;
  614. ShowArrows := ReadBoolean;
  615. FManagerName := ReadString;
  616. end;
  617. end;
  618. procedure TgxBFPSMovement.Loaded;
  619. var
  620. mng: TComponent;
  621. begin
  622. inherited Loaded;
  623. if FManagerName <> '' then
  624. begin
  625. mng := FindManager(TgxFPSMovementManager, FManagerName);
  626. if assigned(mng) then
  627. Manager := TgxFPSMovementManager(mng);
  628. FManagerName := '';
  629. end;
  630. end;
  631. procedure TgxBFPSMovement.setShowArrows(value: boolean);
  632. begin
  633. FShowArrows := value;
  634. dirGl.Visible := value;
  635. if (OwnerBaseSceneObject <> nil) and
  636. not(csDesigning in OwnerBaseSceneObject.ComponentState) then
  637. begin
  638. ArrowLine1.MoveTo(OwnerBaseSceneObject.Parent);
  639. ArrowLine2.MoveTo(OwnerBaseSceneObject.Parent);
  640. ArrowLine3.MoveTo(OwnerBaseSceneObject.Parent);
  641. ArrowLine4.MoveTo(OwnerBaseSceneObject.Parent);
  642. ArrowLine5.MoveTo(OwnerBaseSceneObject.Parent);
  643. ArrowLine6.MoveTo(OwnerBaseSceneObject.Parent);
  644. dirGl.MoveTo(OwnerBaseSceneObject.Parent);
  645. end;
  646. end;
  647. procedure TgxBFPSMovement.MoveForward(Distance: single);
  648. var
  649. prevObj: TgxBaseSceneObject;
  650. begin
  651. Assert(assigned(Manager),
  652. 'Manager not assigned on TgxBFPSMovement behaviour!');
  653. prevObj := Manager.Navigator.MovingObject;
  654. Manager.Navigator.MovingObject := OwnerBaseSceneObject;
  655. Manager.Navigator.MoveForward(Distance);
  656. Manager.Navigator.MovingObject := prevObj;
  657. end;
  658. procedure TgxBFPSMovement.StrafeHorizontal(Distance: single);
  659. var
  660. prevObj: TgxBaseSceneObject;
  661. begin
  662. Assert(assigned(Manager),
  663. 'Manager not assigned on TgxBFPSMovement behaviour!');
  664. prevObj := Manager.Navigator.MovingObject;
  665. Manager.Navigator.MovingObject := OwnerBaseSceneObject;
  666. Manager.Navigator.StrafeHorizontal(Distance);
  667. Manager.Navigator.MovingObject := prevObj;
  668. end;
  669. procedure TgxBFPSMovement.StrafeVertical(Distance: single);
  670. var
  671. prevObj: TgxBaseSceneObject;
  672. begin
  673. Assert(assigned(Manager),
  674. 'Manager not assigned on TgxBFPSMovement behaviour!');
  675. prevObj := Manager.Navigator.MovingObject;
  676. Manager.Navigator.MovingObject := OwnerBaseSceneObject;
  677. Manager.Navigator.StrafeVertical(Distance);
  678. Manager.Navigator.MovingObject := prevObj;
  679. end;
  680. procedure TgxBFPSMovement.TurnHorizontal(Angle: single);
  681. var
  682. prevObj: TgxBaseSceneObject;
  683. begin
  684. Assert(assigned(Manager),
  685. 'Manager not assigned on TgxBFPSMovement behaviour!');
  686. prevObj := Manager.Navigator.MovingObject;
  687. Manager.Navigator.MovingObject := OwnerBaseSceneObject;
  688. Manager.Navigator.TurnHorizontal(Angle);
  689. Manager.Navigator.MovingObject := prevObj;
  690. end;
  691. procedure TgxBFPSMovement.TurnVertical(Angle: single);
  692. var
  693. prevObj: TgxBaseSceneObject;
  694. begin
  695. assert(assigned(Manager),
  696. 'Manager not assigned on TgxBFPSMovement behaviour!');
  697. prevObj := Manager.Navigator.MovingObject;
  698. Manager.Navigator.MovingObject := OwnerBaseSceneObject;
  699. Manager.Navigator.TurnVertical(Angle);
  700. Manager.Navigator.MovingObject := prevObj;
  701. end;
  702. procedure TgxBFPSMovement.Straighten;
  703. var
  704. prevObj: TgxBaseSceneObject;
  705. begin
  706. Assert(assigned(Manager),
  707. 'Manager not assigned on TgxBFPSMovement behaviour!');
  708. prevObj := Manager.Navigator.MovingObject;
  709. Manager.Navigator.MovingObject := OwnerBaseSceneObject;
  710. Manager.Navigator.Straighten;
  711. Manager.Navigator.MovingObject := prevObj;
  712. end;
  713. procedure TgxBFPSMovement.DoProgress(const progressTime: TgxProgressTimes);
  714. var
  715. newPosition: TVector4f;
  716. CollisionState: TCollisionState;
  717. begin
  718. inherited DoProgress(progressTime);
  719. Assert(assigned(Manager), 'FPS Manager not assigned to behaviour.');
  720. // make arrowlines invisible (they are made visible in SphereSweepAndSlide)
  721. ArrowLine1.Visible := false;
  722. ArrowLine2.Visible := false;
  723. ArrowLine3.Visible := false;
  724. ArrowLine4.Visible := false;
  725. ArrowLine5.Visible := false;
  726. ArrowLine6.Visible := false;
  727. CollisionState := TCollisionState.Create();
  728. CollisionState.Position := oldPosition;
  729. CollisionStates.add(CollisionState);
  730. // this is the position we are trying to move to with controls
  731. newPosition := OwnerBaseSceneObject.Position.AsVector;
  732. // Change in position = velocity * time taken
  733. if GravityEnabled then
  734. newPosition.Y := newPosition.Y - Manager.MovementScale * 0.5 *
  735. progressTime.deltaTime;
  736. // do some magic!!! and store new position in newPosition
  737. if sphereRadius < 0 then
  738. Manager.SphereSweepAndSlide(self, oldPosition, Velocity, newPosition,
  739. OwnerBaseSceneObject.boundingSphereRadius)
  740. else
  741. Manager.SphereSweepAndSlide(self, oldPosition, Velocity, newPosition,
  742. sphereRadius);
  743. OwnerBaseSceneObject.Position.AsVector := newPosition;
  744. oldPosition := newPosition;
  745. if CollisionStates.count > 0 then
  746. begin
  747. CollisionState := TCollisionState(CollisionStates.First);
  748. TickCount := TThread.GetTickCount();
  749. // remove all old states
  750. while (CollisionState <> nil) and
  751. (CollisionState.Time < tickCount - Manager.DisplayTime) do
  752. begin
  753. CollisionStates.Remove(CollisionState);
  754. CollisionState.Free;
  755. if CollisionStates.count = 0 then
  756. exit;
  757. CollisionState := TCollisionState(CollisionStates.First);
  758. end;
  759. end;
  760. end;
  761. procedure TgxBFPSMovement.RenderArrowLines(Sender: TObject;
  762. var rci: TgxRenderContextInfo);
  763. var
  764. x, y, z, t: single;
  765. i: integer;
  766. CollisionState: TCollisionState;
  767. begin
  768. // caption:= IntToStr(CollisionStates.Count);
  769. glColor3f(1, 1, 1);
  770. rci.gxStates.Disable(stLighting);
  771. // draw position trail
  772. glBegin(GL_LINE_STRIP);
  773. for i := 0 to CollisionStates.count - 1 do
  774. begin
  775. CollisionState := TCollisionState(CollisionStates.Items[i]);
  776. x := CollisionState.Position.X;
  777. y := CollisionState.Position.Y;
  778. z := CollisionState.Position.Z;
  779. glVertex3f(x, y, z);
  780. end;
  781. glEnd();
  782. // draw normals trail
  783. glBegin(GL_LINES);
  784. for i := 0 to CollisionStates.count - 1 do
  785. begin
  786. CollisionState := TCollisionState(CollisionStates.Items[i]);
  787. t := (Manager.DisplayTime - (tickCount - CollisionState.Time)) /
  788. Manager.DisplayTime;
  789. glColor3f(t, t, t);
  790. glVertex3f(CollisionState.Contact.intPoint.X,
  791. CollisionState.Contact.intPoint.Y, CollisionState.Contact.intPoint.Z);
  792. glVertex3f(CollisionState.Contact.intPoint.X +
  793. CollisionState.Contact.intNormal.X, //VKSphere4.Radius,
  794. CollisionState.Contact.intPoint.Y + CollisionState.Contact.intNormal.Y,
  795. //VKSphere4.Radius,
  796. CollisionState.Contact.intPoint.Z + CollisionState.Contact.intNormal.Z);
  797. //VKSphere4.Radius);
  798. end;
  799. glEnd();
  800. end;
  801. // ------------------------------------------------------------------
  802. // ------------------------------------------------------------------
  803. // ------------------------------------------------------------------
  804. initialization
  805. // ------------------------------------------------------------------
  806. // ------------------------------------------------------------------
  807. // ------------------------------------------------------------------
  808. RegisterXCollectionItemClass(TgxMapCollectionItem);
  809. RegisterXCollectionItemClass(TgxBFPSMovement);
  810. finalization
  811. UnregisterXCollectionItemClass(TgxMapCollectionItem);
  812. UnregisterXCollectionItemClass(TgxBFPSMovement);
  813. end.