GLS.FPSMovement.pas 27 KB

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