GLX.DCE.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. // *
  2. // The graphics platform GLXcene https://github.com/glscene
  3. //
  4. unit GLX.DCE;
  5. (*
  6. How to use:
  7. - Add a DCEManager to you form and configure its properties
  8. - Add a Dynamic Collision Behavior to you object
  9. - Add a Static Collision behaviour to objects which yours will collide
  10. - You can choose the shape of your static object
  11. - csEllipsoid, csBox
  12. - csFreeform MUST BE A TgxFreeform, otherwise will raise errors
  13. - csTerrain MUST BE A TgxTerrainRenderer, same condition above
  14. - Active: Disable or enable the behaviour for this object
  15. - Friction: is a value aprox. between 0 (no friction) and 100 (no movement)
  16. - Layer: An object collides only with lower or equal layers
  17. - Size: is used for Ellipsoids (Radius) / Boxes (Dimensions)
  18. - Solid: Object still generate collision events but it doesn't "block" the dynamic object
  19. - UseGravity: You can disable the gravity for that object
  20. - SlideOrBounce: The object can bounce like a ball or slide like an FPS
  21. - BounceFactor: Restituition factor, 1 means that it will bounce forever
  22. *)
  23. interface
  24. {$I Scene.inc}
  25. uses
  26. System.Classes,
  27. System.SysUtils,
  28. GLX.XCollection,
  29. GLX.VectorGeometry,
  30. GLX.VectorLists,
  31. GLX.BaseClasses,
  32. GLX.Manager,
  33. GLX.VectorTypes,
  34. Scene.Strings,
  35. GLX.Scene,
  36. GLX.VectorFileObjects,
  37. GLX.DCEMisc,
  38. GLX.EllipseCollision,
  39. GLX.TerrainRenderer,
  40. GLX.Coordinates;
  41. type
  42. // Only csEllipsoid can have dynamic behaviour
  43. TgxDCEShape = (csEllipsoid, csBox, csFreeform, csTerrain);
  44. (* Indicates which type of layer comparison is made when trying to detect
  45. collisions between 2 bodies (A and B). Possible values are:
  46. ccsDCEStandard: Collides bodies if A.layer <= B.layer
  47. ccsCollisionStandard: Collides bodies if either A or B have
  48. layer equal to zero or if their layers are different.
  49. ccsHybrid: Collides bodies if either one of the previous
  50. checks would pass (i.e. if the layer of either body is
  51. equal to 0 or if A.layer <= B.layer) *and* if both
  52. layers are positive (that is, turns off collision
  53. for bodies whose layer is < 0) *)
  54. TgxDCECollisionSelection = (ccsDCEStandard, ccsCollisionStandard, ccsHybrid);
  55. TgxDCECollision = record
  56. Position: TAffineVector;
  57. Normal: TAffineVector; // Surface normal
  58. Bounce: TAffineVector; // Surface reflection
  59. Nearest: Boolean;
  60. RootCollision: Boolean;
  61. Distance: single;
  62. end;
  63. TgxDCEStatic = class;
  64. TgxDCEDynamic = class;
  65. TgxDCECollisionEvent = procedure(Sender: TObject;
  66. object1, object2: TgxBaseSceneObject; CollisionInfo: TgxDCECollision)
  67. of object;
  68. TgxDCEObjectCollisionEvent = procedure(Sender: TObject;
  69. ObjectCollided: TgxBaseSceneObject; CollisionInfo: TgxDCECollision) of object;
  70. TgxDCEManager = class(TComponent)
  71. private
  72. FStatics: TList;
  73. FDynamics: TList;
  74. FGravity: single;
  75. FWorldDirection: TgxCoordinates; // Used to calculate jumps f.i.
  76. FWorldScale: single;
  77. FMovimentScale: single;
  78. FStandardiseLayers: TgxDCECollisionSelection;
  79. FManualStep: Boolean;
  80. FOnCollision: TgxDCECollisionEvent;
  81. procedure SetWorldDirection(const Value: TgxCoordinates);
  82. procedure SetWorldScale(const Value: single);
  83. function GetDynamicCount: Integer;
  84. function GetStaticCount: Integer;
  85. protected
  86. procedure RegisterStatic(aClient: TgxDCEStatic);
  87. procedure DeRegisterStatic(aClient: TgxDCEStatic);
  88. procedure DeRegisterAllStatics;
  89. procedure RegisterDynamic(aClient: TgxDCEDynamic);
  90. procedure DeRegisterDynamic(aClient: TgxDCEDynamic);
  91. procedure DeRegisterAllDynamics;
  92. public
  93. constructor Create(AOwner: TComponent); override;
  94. destructor Destroy; override;
  95. // Moves the body by the distance and returns the average friction
  96. function MoveByDistance(var Body: TgxDCEDynamic;
  97. deltaS, deltaAbsS: TAffineVector): single;
  98. procedure Step(deltaTime: Double);
  99. property DynamicCount: Integer read GetDynamicCount;
  100. property StaticCount: Integer read GetStaticCount;
  101. published
  102. property Gravity: single read FGravity write FGravity;
  103. property WorldDirection: TgxCoordinates read FWorldDirection write SetWorldDirection;
  104. property WorldScale: single read FWorldScale write SetWorldScale;
  105. property MovimentScale: single read FMovimentScale write FMovimentScale;
  106. Property StandardiseLayers: TgxDCECollisionSelection read FStandardiseLayers write FStandardiseLayers;
  107. Property ManualStep: Boolean read FManualStep write FManualStep;
  108. property OnCollision: TgxDCECollisionEvent read FOnCollision write FOnCollision;
  109. end;
  110. TgxDCEStatic = class(TgxBehaviour)
  111. private
  112. FManager: TgxDCEManager;
  113. FManagerName: String; // NOT persistent, temporarily used for persistence
  114. FActive: Boolean;
  115. FShape: TgxDCEShape;
  116. // Collides only with lower or equal layers
  117. FLayer: Integer;
  118. // Collide and slide if true, otherwise it "walk thru walls"
  119. FSolid: Boolean;
  120. FFriction: single; // 0 (no friction); 100 (no movement)
  121. FBounceFactor: single; // 0 (don't bounce); 1 (bounce forever)
  122. FSize: TgxCoordinates;
  123. // Events
  124. FOnCollision: TgxDCEObjectCollisionEvent;
  125. procedure SetShape(const Value: TgxDCEShape);
  126. procedure SetFriction(const Value: single);
  127. procedure SetBounceFactor(const Value: single);
  128. procedure SetSize(const Value: TgxCoordinates);
  129. protected
  130. procedure SetManager(const val: TgxDCEManager);
  131. procedure WriteToFiler(writer: TWriter); override;
  132. procedure ReadFromFiler(reader: TReader); override;
  133. procedure Loaded; override;
  134. public
  135. constructor Create(AOwner: TXCollection); override;
  136. destructor Destroy; override;
  137. procedure Assign(Source: TPersistent); override;
  138. class function FriendlyName: String; override;
  139. class function FriendlyDescription: String; override;
  140. property OnCollision: TgxDCEObjectCollisionEvent read FOnCollision write FOnCollision;
  141. published
  142. property Active: Boolean read FActive write FActive;
  143. property Manager: TgxDCEManager read FManager write SetManager;
  144. property Shape: TgxDCEShape read FShape write SetShape;
  145. property Layer: Integer read FLayer write FLayer;
  146. property Solid: Boolean read FSolid write FSolid;
  147. property Friction: single read FFriction write SetFriction;
  148. property BounceFactor: single read FBounceFactor write SetBounceFactor;
  149. property Size: TgxCoordinates read FSize write SetSize;
  150. end;
  151. TgxDCESlideOrBounce = (csbSlide, csbBounce);
  152. TgxDCEDynamic = class(TgxBehaviour)
  153. private
  154. FManager: TgxDCEManager;
  155. FManagerName: String; // NOT persistent, temporarily used for persistence
  156. FActive: Boolean;
  157. FUseGravity: Boolean;
  158. FLayer: Integer; // Collides only with lower or equal layers
  159. FSolid: Boolean;
  160. // Collide and slide if true, otherwise it "walk thru walls"
  161. FFriction: single; // 0 (no friction); 100 (no movement)
  162. FBounceFactor: single; // 0 (don't bounce); 1 (bounce forever)
  163. FSize: TgxCoordinates;
  164. //Number of iterations of the collision method
  165. FMaxRecursionDepth: byte;
  166. FSlideOrBounce: TgxDCESlideOrBounce; // gak20041122
  167. // Movement
  168. FAccel: TAffineVector; // Current acceleration
  169. FSpeed: TAffineVector; // Current speed
  170. FAbsAccel: TAffineVector; // Current absolute accel
  171. FAbsSpeed: TAffineVector; // Current absolute speed
  172. FGravSpeed: TAffineVector; // Current gravity speed
  173. FTotalFriction: single; // Current sum of all contatcs friction
  174. FInGround: Boolean;
  175. FGroundNormal: TAffineVector;
  176. FJumpHeight, FJumpForce, FJumpSpeed: single;
  177. FJumping: Boolean;
  178. // Events
  179. FOnCollision: TgxDCEObjectCollisionEvent;
  180. procedure SetFriction(const Value: single);
  181. procedure SetBounceFactor(const Value: single);
  182. procedure SetSize(const Value: TgxCoordinates);
  183. protected
  184. procedure SetManager(const val: TgxDCEManager);
  185. procedure WriteToFiler(writer: TWriter); override;
  186. procedure ReadFromFiler(reader: TReader); override;
  187. procedure Loaded; override;
  188. public
  189. constructor Create(AOwner: TXCollection); override;
  190. destructor Destroy; override;
  191. procedure Assign(Source: TPersistent); override;
  192. class function FriendlyName: String; override;
  193. class function FriendlyDescription: String; override;
  194. procedure ApplyAccel(NewAccel: TAffineVector); overload;
  195. procedure ApplyAccel(x, y, z: single); overload;
  196. procedure ApplyAbsAccel(NewAccel: TAffineVector); overload;
  197. procedure ApplyAbsAccel(x, y, z: single); overload;
  198. procedure StopAccel;
  199. procedure StopAbsAccel;
  200. procedure Jump(jHeight, jSpeed: single);
  201. procedure Move(deltaS: TAffineVector; deltaTime: Double);
  202. procedure MoveTo(Position: TAffineVector; Amount: single);
  203. procedure DoMove(deltaTime: Double);
  204. procedure DoProgress(const progressTime: TgxProgressTimes); override;
  205. // Runtime only
  206. property Speed: TAffineVector read FSpeed write FSpeed;
  207. property InGround: Boolean read FInGround;
  208. property MaxRecursionDepth: byte read FMaxRecursionDepth write FMaxRecursionDepth;
  209. property OnCollision: TgxDCEObjectCollisionEvent read FOnCollision write FOnCollision;
  210. published
  211. property Active: Boolean read FActive write FActive;
  212. property Manager: TgxDCEManager read FManager write SetManager;
  213. property UseGravity: Boolean read FUseGravity write FUseGravity;
  214. property Layer: Integer read FLayer write FLayer;
  215. property Solid: Boolean read FSolid write FSolid;
  216. property Friction: single read FFriction write SetFriction;
  217. property BounceFactor: single read FBounceFactor write SetBounceFactor;
  218. property Size: TgxCoordinates read FSize write SetSize;
  219. property SlideOrBounce: TgxDCESlideOrBounce read FSlideOrBounce write FSlideOrBounce;
  220. end;
  221. function GetOrCreateDCEStatic(behaviours: TgxBehaviours): TgxDCEStatic; overload;
  222. function GetOrCreateDCEStatic(obj: TgxBaseSceneObject): TgxDCEStatic; overload;
  223. function GetOrCreateDCEDynamic(behaviours: TgxBehaviours): TgxDCEDynamic; overload;
  224. function GetOrCreateDCEDynamic(obj: TgxBaseSceneObject): TgxDCEDynamic; overload;
  225. // -------------------------------------------------------------------
  226. implementation
  227. // -------------------------------------------------------------------
  228. function RotateVectorByObject(obj: TgxBaseSceneObject; v: TAffineVector): TAffineVector;
  229. var
  230. v2: TgxVector;
  231. begin
  232. SetVector(v2, v);
  233. SetVector(result, VectorTransform(v2, obj.Matrix^));
  234. end;
  235. constructor TgxDCEManager.Create(AOwner: TComponent);
  236. begin
  237. inherited Create(AOwner);
  238. FStatics := TList.Create;
  239. FDynamics := TList.Create;
  240. FGravity := 0;
  241. FWorldDirection := TgxCoordinates.CreateInitialized(Self, YHmgVector, csVector);
  242. FWorldScale := 1;
  243. FMovimentScale := 1;
  244. FStandardiseLayers := ccsDCEStandard;
  245. FManualStep := False;
  246. RegisterManager(Self);
  247. end;
  248. destructor TgxDCEManager.Destroy;
  249. begin
  250. DeRegisterAllStatics;
  251. DeRegisterAllDynamics;
  252. DeRegisterManager(Self);
  253. FStatics.Free;
  254. FDynamics.Free;
  255. FWorldDirection.Free;
  256. inherited Destroy;
  257. end;
  258. function TgxDCEManager.GetDynamicCount: Integer;
  259. begin
  260. result := FDynamics.Count;
  261. end;
  262. function TgxDCEManager.GetStaticCount: Integer;
  263. begin
  264. result := FStatics.Count;
  265. end;
  266. function TgxDCEManager.MoveByDistance(var Body: TgxDCEDynamic;
  267. deltaS, deltaAbsS: TAffineVector): single;
  268. var
  269. // Friction and bounce
  270. TotalFriction, Bounce, f, m, restitution: single;
  271. ContactList: TgxIntegerList;
  272. // Temporary properties (Static or Dynamic)
  273. tFriction, tBounceFactor: single;
  274. TObject: TgxBaseSceneObject;
  275. // Collision results
  276. ColInfo: TgxDCECollision;
  277. lastobj: Integer;
  278. i, oi: Integer;
  279. MP: TECMovePack;
  280. CanCollide, GravCollided: Boolean;
  281. // Vars used to calculate high velocities
  282. ColRange, MaxRange: single;
  283. dCR, dT, deltaCR: Double;
  284. begin
  285. // Set collider parameters
  286. MP.Radius := Body.Size.AsAffineVector;
  287. MP.Position := AffineVectorMake(Body.OwnerBaseSceneObject.AbsolutePosition);
  288. MP.Velocity := deltaS;
  289. MP.Gravity := deltaAbsS;
  290. MP.ObjectInfo.Solid := Body.Solid;
  291. MP.UnitScale := FWorldScale;
  292. MP.MaxRecursionDepth := Body.MaxRecursionDepth;
  293. // Get collision range, if it is too big separate into small pieces
  294. ECSetCollisionRange(MP);
  295. ColRange := MP.CollisionRange;
  296. deltaCR := ColRange;
  297. MaxRange := MaxXYZComponent(MP.Radius) * 2.1;
  298. SetLength(MP.Contacts, 0);
  299. GravCollided := False; // Is colliding with the ground
  300. Body.FGroundNormal := NullVector;
  301. while deltaCR > 0 do
  302. begin
  303. if deltaCR > MaxRange then
  304. begin
  305. dCR := MaxRange;
  306. deltaCR := deltaCR - MaxRange;
  307. end
  308. else
  309. begin
  310. dCR := deltaCR;
  311. deltaCR := 0;
  312. end;
  313. dT := dCR / ColRange;
  314. MP.Velocity := VectorScale(deltaS, dT);
  315. MP.Gravity := VectorScale(deltaAbsS, dT);
  316. ECSetCollisionRange(MP);
  317. ECResetColliders(MP);
  318. // For each static collider
  319. for i := 0 to FStatics.Count - 1 do
  320. with TgxDCEStatic(FStatics[i]) do
  321. begin
  322. CanCollide := False;
  323. if (Active) then
  324. case FStandardiseLayers of
  325. ccsDCEStandard:
  326. CanCollide := (Layer <= Body.Layer);
  327. ccsCollisionStandard:
  328. CanCollide := (Layer = 0) or (Body.Layer = 0) or
  329. (Layer <> Body.Layer);
  330. ccsHybrid:
  331. CanCollide := ((Layer = 0) or (Body.Layer = 0) or
  332. (Layer <= Body.Layer)) and (Layer >= 0) and (Body.Layer >= 0);
  333. end;
  334. // Add colliders to move pack
  335. if CanCollide then
  336. begin
  337. case Shape of
  338. csFreeform:
  339. ECAddFreeForm(MP, OwnerBaseSceneObject, Solid, i);
  340. csEllipsoid:
  341. ECAddEllipsoid(MP,
  342. AffineVectorMake(OwnerBaseSceneObject.AbsolutePosition),
  343. Size.AsAffineVector, Solid, i);
  344. csBox:
  345. ECAddBox(MP, OwnerBaseSceneObject, Size.AsAffineVector, Solid, i);
  346. csTerrain:
  347. ECAddTerrain(MP, TgxTerrainRenderer(OwnerBaseSceneObject),
  348. FWorldScale * 2, Solid, i);
  349. end;
  350. end;
  351. end;
  352. // For each dynamic collider add a static ellipsoid
  353. for i := 0 to FDynamics.Count - 1 do
  354. with TgxDCEDynamic(FDynamics[i]) do
  355. begin
  356. CanCollide := False;
  357. if (Active) and (TgxDCEDynamic(FDynamics[i]) <> Body) then
  358. case FStandardiseLayers of
  359. ccsDCEStandard:
  360. CanCollide := (Layer <= Body.Layer);
  361. ccsCollisionStandard:
  362. CanCollide := (Layer = 0) or (Body.Layer = 0) or
  363. (Layer <> Body.Layer);
  364. ccsHybrid:
  365. CanCollide := ((Layer = 0) or (Body.Layer = 0) or
  366. (Layer <= Body.Layer)) and (Layer >= 0) and (Body.Layer >= 0);
  367. end;
  368. // Add collider to move pack
  369. // To differ from static it is added with a negative ID (id < 0)
  370. if CanCollide then
  371. ECAddEllipsoid(MP,
  372. AffineVectorMake(OwnerBaseSceneObject.AbsolutePosition),
  373. Size.AsAffineVector, Solid, -1 - i);
  374. end;
  375. CollideAndSlide(MP);
  376. if MP.GravityCollided then
  377. begin
  378. GravCollided := True;
  379. Body.FGroundNormal := MP.GroundNormal;
  380. end;
  381. MP.Position := MP.ResultPos;
  382. end;
  383. // Set the result
  384. Body.OwnerBaseSceneObject.AbsolutePosition := VectorMake(MP.ResultPos);
  385. Body.FInGround := GravCollided;
  386. // Generate events and calculate average friction
  387. lastobj := -1;
  388. TotalFriction := Body.Friction;
  389. ContactList := TgxIntegerList.Create;
  390. try
  391. for i := 0 to High(MP.Contacts) do
  392. with MP do
  393. begin
  394. oi := Contacts[i].ObjectInfo.ObjectID;
  395. // Don't repeat objects with same ID
  396. if ContactList.IndexOf(oi) >= 0 then
  397. Continue
  398. else
  399. ContactList.Add(oi);
  400. // Check if it is static or dynamic
  401. if oi < 0 then
  402. begin
  403. tFriction := TgxDCEDynamic(FDynamics[abs(oi) - 1]).Friction;
  404. tBounceFactor := TgxDCEDynamic(FDynamics[abs(oi) - 1]).BounceFactor;
  405. TObject := TgxDCEDynamic(FDynamics[abs(oi) - 1]).OwnerBaseSceneObject;
  406. end
  407. else
  408. begin
  409. tFriction := TgxDCEStatic(FStatics[oi]).Friction;
  410. tBounceFactor := TgxDCEStatic(FStatics[oi]).BounceFactor;
  411. TObject := TgxDCEStatic(FStatics[oi]).OwnerBaseSceneObject;
  412. end;
  413. TotalFriction := TotalFriction + tFriction;
  414. ColInfo.Position := Contacts[i].Position;
  415. ColInfo.Normal := Contacts[i].SurfaceNormal;
  416. ColInfo.Bounce := VectorNormalize
  417. (VectorReflect(VectorAdd(deltaS, deltaAbsS), ColInfo.Normal));
  418. ColInfo.Nearest := oi = MP.NearestObject;
  419. // Calculate bounce
  420. if (Body.SlideOrBounce = csbBounce) and ColInfo.Nearest then
  421. begin
  422. Bounce := VectorDotProduct(Body.FSpeed, ColInfo.Normal);
  423. if Bounce < 0 then
  424. begin
  425. restitution := (Body.BounceFactor + tBounceFactor) / 2;
  426. m := VectorLength(Body.FSpeed);
  427. f := -Bounce / VectorNorm(ColInfo.Normal) * (1 + restitution);
  428. CombineVector(Body.FSpeed, ColInfo.Normal, f);
  429. // Limit bounce speed
  430. if VectorLength(Body.FSpeed) > m * 2 then
  431. Body.FSpeed := NullVector;
  432. end;
  433. Bounce := VectorDotProduct(Body.FAbsSpeed, ColInfo.Normal);
  434. if Bounce < 0 then
  435. begin
  436. restitution := (Body.BounceFactor + tBounceFactor) / 2;
  437. m := VectorLength(Body.FAbsSpeed);
  438. f := -Bounce / VectorNorm(ColInfo.Normal) * (1 + restitution);
  439. CombineVector(Body.FAbsSpeed, ColInfo.Normal, f);
  440. // Limit
  441. if VectorLength(Body.FAbsSpeed) > m * 2 then
  442. Body.FAbsSpeed := NullVector;
  443. end;
  444. Bounce := VectorDotProduct(Body.FGravSpeed, ColInfo.Normal);
  445. if Bounce < 0 then
  446. begin
  447. restitution := (Body.BounceFactor + tBounceFactor) / 2;
  448. m := VectorLength(Body.FGravSpeed);
  449. f := -Bounce / VectorNorm(ColInfo.Normal) * (1 + restitution);
  450. CombineVector(Body.FGravSpeed, ColInfo.Normal, f);
  451. // Limit
  452. if VectorLength(Body.FGravSpeed) > m * 2 then
  453. Body.FGravSpeed := NullVector;
  454. end;
  455. end;
  456. ColInfo.RootCollision := (lastobj <> oi);
  457. ColInfo.Distance := Contacts[i].Distance;
  458. lastobj := oi;
  459. if Assigned(FOnCollision) then
  460. FOnCollision(Self, Body.OwnerBaseSceneObject, TObject, ColInfo);
  461. if Assigned(Body.FOnCollision) then
  462. Body.FOnCollision(Self, TObject, ColInfo);
  463. if Assigned(Body.FOnCollision) then
  464. Body.FOnCollision(Self, TObject, ColInfo);
  465. // If the collided object is static trigger its event
  466. if (oi >= 0) and Assigned(TgxDCEStatic(FStatics[oi]).FOnCollision) then
  467. TgxDCEStatic(FStatics[oi]).FOnCollision(Self,
  468. Body.OwnerBaseSceneObject, ColInfo);
  469. end;
  470. finally
  471. ContactList.Free;
  472. end;
  473. result := TotalFriction;
  474. end;
  475. procedure TgxDCEManager.Step(deltaTime: Double);
  476. var
  477. i: Integer;
  478. begin
  479. if deltaTime > 0.1 then
  480. deltaTime := 0.1;
  481. for i := 0 to FDynamics.Count - 1 do
  482. with TgxDCEDynamic(FDynamics[i]) do
  483. if Active then
  484. DoMove(deltaTime);
  485. end;
  486. procedure TgxDCEManager.SetWorldDirection(const Value: TgxCoordinates);
  487. begin
  488. FWorldDirection := Value;
  489. FWorldDirection.Normalize;
  490. end;
  491. procedure TgxDCEManager.SetWorldScale(const Value: single);
  492. begin
  493. if Value = 0 then
  494. FWorldScale := 0.001
  495. else if Value < 0 then
  496. FWorldScale := abs(Value)
  497. else
  498. FWorldScale := Value;
  499. end;
  500. procedure TgxDCEManager.RegisterStatic(aClient: TgxDCEStatic);
  501. begin
  502. if Assigned(aClient) then
  503. if FStatics.IndexOf(aClient) < 0 then
  504. begin
  505. FStatics.Add(aClient);
  506. aClient.FManager := Self;
  507. end;
  508. end;
  509. procedure TgxDCEManager.DeRegisterStatic(aClient: TgxDCEStatic);
  510. begin
  511. if Assigned(aClient) then
  512. begin
  513. aClient.FManager := nil;
  514. FStatics.Remove(aClient);
  515. end;
  516. end;
  517. procedure TgxDCEManager.DeRegisterAllStatics;
  518. var
  519. i: Integer;
  520. begin
  521. // Fast deregistration
  522. for i := 0 to FStatics.Count - 1 do
  523. TgxDCEStatic(FStatics[i]).FManager := nil;
  524. FStatics.Clear;
  525. end;
  526. procedure TgxDCEManager.RegisterDynamic(aClient: TgxDCEDynamic);
  527. begin
  528. if Assigned(aClient) then
  529. if FDynamics.IndexOf(aClient) < 0 then
  530. begin
  531. FDynamics.Add(aClient);
  532. aClient.FManager := Self;
  533. end;
  534. end;
  535. procedure TgxDCEManager.DeRegisterDynamic(aClient: TgxDCEDynamic);
  536. begin
  537. if Assigned(aClient) then
  538. begin
  539. aClient.FManager := nil;
  540. FDynamics.Remove(aClient);
  541. end;
  542. end;
  543. procedure TgxDCEManager.DeRegisterAllDynamics;
  544. var
  545. i: Integer;
  546. begin
  547. // Fast deregistration
  548. for i := 0 to FDynamics.Count - 1 do
  549. TgxDCEDynamic(FDynamics[i]).FManager := nil;
  550. FDynamics.Clear;
  551. end;
  552. //---------------------------------------
  553. // TgxDCEStatic
  554. //---------------------------------------
  555. procedure TgxDCEStatic.Assign(Source: TPersistent);
  556. begin
  557. if Source is TgxDCEStatic then
  558. begin
  559. Active := TgxDCEStatic(Source).Active;
  560. Manager := TgxDCEStatic(Source).Manager;
  561. Shape := TgxDCEStatic(Source).Shape;
  562. Layer := TgxDCEStatic(Source).Layer;
  563. Solid := TgxDCEStatic(Source).Solid;
  564. Size.Assign(TgxDCEStatic(Source).Size);
  565. Friction := TgxDCEStatic(Source).Friction;
  566. BounceFactor := TgxDCEStatic(Source).BounceFactor;
  567. end;
  568. inherited Assign(Source);
  569. end;
  570. constructor TgxDCEStatic.Create(AOwner: TXCollection);
  571. begin
  572. inherited Create(AOwner);
  573. FActive := True;
  574. FSize := TgxCoordinates.CreateInitialized(Self, XYZHmgVector, csVector);
  575. FShape := csEllipsoid;
  576. FSolid := True;
  577. FFriction := 1;
  578. FBounceFactor := 0;
  579. end;
  580. destructor TgxDCEStatic.Destroy;
  581. begin
  582. Manager := nil;
  583. FSize.Free;
  584. inherited Destroy;
  585. end;
  586. class function TgxDCEStatic.FriendlyDescription: String;
  587. begin
  588. result := 'Static Collision-detection registration';
  589. end;
  590. class function TgxDCEStatic.FriendlyName: String;
  591. begin
  592. result := 'DCE Static Collider';
  593. end;
  594. procedure TgxDCEStatic.Loaded;
  595. var
  596. mng: TComponent;
  597. begin
  598. inherited;
  599. if FManagerName <> '' then
  600. begin
  601. mng := FindManager(TgxDCEManager, FManagerName);
  602. if Assigned(mng) then
  603. Manager := TgxDCEManager(mng);
  604. FManagerName := '';
  605. end;
  606. end;
  607. procedure TgxDCEStatic.WriteToFiler(writer: TWriter);
  608. begin
  609. with writer do
  610. begin
  611. // ArchiveVersion 1, added inherited call
  612. WriteInteger(1);
  613. inherited;
  614. if Assigned(FManager) then
  615. WriteString(FManager.GetNamePath)
  616. else
  617. WriteString('');
  618. WriteInteger(Integer(FShape));
  619. WriteInteger(FLayer);
  620. WriteBoolean(FSolid);
  621. WriteBoolean(FActive);
  622. WriteSingle(FFriction);
  623. WriteSingle(FBounceFactor);
  624. FSize.WriteToFiler(writer);
  625. end;
  626. end;
  627. procedure TgxDCEStatic.ReadFromFiler(reader: TReader);
  628. var
  629. archiveVersion: Integer;
  630. begin
  631. with reader do
  632. begin
  633. archiveVersion := ReadInteger;
  634. Assert(archiveVersion in [0 .. 1]);
  635. if archiveVersion >= 1 then
  636. inherited;
  637. FManagerName := ReadString;
  638. Manager := nil;
  639. FShape := TgxDCEShape(ReadInteger);
  640. FLayer := ReadInteger;
  641. FSolid := ReadBoolean;
  642. FActive := ReadBoolean;
  643. FFriction := ReadSingle;
  644. FBounceFactor := ReadSingle;
  645. FSize.ReadFromFiler(reader);
  646. end;
  647. end;
  648. procedure TgxDCEStatic.SetBounceFactor(const Value: single);
  649. begin
  650. FBounceFactor := Value;
  651. if FBounceFactor < 0 then
  652. FBounceFactor := 0;
  653. if FBounceFactor > 1 then
  654. FBounceFactor := 1;
  655. end;
  656. procedure TgxDCEStatic.SetFriction(const Value: single);
  657. begin
  658. FFriction := Value;
  659. if FFriction < 0 then
  660. FFriction := 0;
  661. if FFriction > 100 then
  662. FFriction := 100;
  663. end;
  664. procedure TgxDCEStatic.SetManager(const val: TgxDCEManager);
  665. begin
  666. if val <> FManager then
  667. begin
  668. if Assigned(FManager) then
  669. FManager.DeRegisterStatic(Self);
  670. if Assigned(val) then
  671. val.RegisterStatic(Self);
  672. end;
  673. end;
  674. procedure TgxDCEStatic.SetShape(const Value: TgxDCEShape);
  675. begin
  676. FShape := Value;
  677. end;
  678. procedure TgxDCEStatic.SetSize(const Value: TgxCoordinates);
  679. begin
  680. FSize.Assign(Value);
  681. if FSize.x <= 0 then
  682. FSize.x := 0.1;
  683. if FSize.y <= 0 then
  684. FSize.y := 0.1;
  685. if FSize.z <= 0 then
  686. FSize.z := 0.1;
  687. end;
  688. //-------------------------------------
  689. // TgxDCEDynamic
  690. //-------------------------------------
  691. procedure TgxDCEDynamic.ApplyAccel(NewAccel: TAffineVector);
  692. begin
  693. AddVector(FAccel, NewAccel);
  694. end;
  695. procedure TgxDCEDynamic.ApplyAccel(x, y, z: single);
  696. begin
  697. AddVector(FAccel, AffineVectorMake(x, y, z));
  698. end;
  699. procedure TgxDCEDynamic.ApplyAbsAccel(NewAccel: TAffineVector);
  700. begin
  701. AddVector(FAbsAccel, NewAccel);
  702. end;
  703. procedure TgxDCEDynamic.ApplyAbsAccel(x, y, z: single);
  704. begin
  705. AddVector(FAbsAccel, AffineVectorMake(x, y, z));
  706. end;
  707. procedure TgxDCEDynamic.StopAccel;
  708. begin
  709. SetVector(FAccel, NullVector);
  710. end;
  711. procedure TgxDCEDynamic.StopAbsAccel;
  712. begin
  713. SetVector(FAbsAccel, NullVector);
  714. end;
  715. procedure TgxDCEDynamic.Assign(Source: TPersistent);
  716. begin
  717. if Source is TgxDCEDynamic then
  718. begin
  719. Manager := TgxDCEDynamic(Source).Manager;
  720. Active := TgxDCEDynamic(Source).Active;
  721. UseGravity := TgxDCEDynamic(Source).UseGravity;
  722. Layer := TgxDCEDynamic(Source).Layer;
  723. Solid := TgxDCEDynamic(Source).Solid;
  724. Size.Assign(TgxDCEDynamic(Source).Size);
  725. Friction := TgxDCEDynamic(Source).Friction;
  726. BounceFactor := TgxDCEDynamic(Source).BounceFactor;
  727. SlideOrBounce := TgxDCEDynamic(Source).SlideOrBounce;
  728. MaxRecursionDepth := TgxDCEDynamic(Source).MaxRecursionDepth;
  729. end;
  730. inherited Assign(Source);
  731. end;
  732. constructor TgxDCEDynamic.Create(AOwner: TXCollection);
  733. begin
  734. inherited Create(AOwner);
  735. FActive := True;
  736. FUseGravity := True;
  737. FSize := TgxCoordinates.CreateInitialized(Self, XYZHmgVector, csVector);
  738. FSolid := True;
  739. FFriction := 1;
  740. FBounceFactor := 0;
  741. FMaxRecursionDepth := 5;
  742. FSlideOrBounce := csbSlide;
  743. FInGround := False;
  744. FAccel := NullVector;
  745. FAbsAccel := NullVector;
  746. FSpeed := NullVector;
  747. FAbsSpeed := NullVector;
  748. FGravSpeed := NullVector;
  749. end;
  750. destructor TgxDCEDynamic.Destroy;
  751. begin
  752. Manager := nil;
  753. FSize.Free;
  754. inherited Destroy;
  755. end;
  756. procedure TgxDCEDynamic.DoMove(deltaTime: Double);
  757. var
  758. fGround, fAir, G: single;
  759. v, deltaS, deltaAbsS: TAffineVector;
  760. procedure Accel(var aSpeed: TAffineVector; aFric: single;
  761. aForce: TAffineVector);
  762. begin
  763. ScaleVector(aForce, deltaTime);
  764. ScaleVector(aSpeed, aFric);
  765. aSpeed := VectorAdd(aForce, aSpeed);
  766. end;
  767. begin
  768. if (FSlideOrBounce = csbBounce) then
  769. FAccel := RotateVectorByObject(OwnerBaseSceneObject, FAccel);
  770. // Ground friction
  771. fGround := 1 - deltaTime * FTotalFriction;
  772. if fGround < 0 then
  773. fGround := 0;
  774. // Air friction
  775. fAir := 1 - deltaTime * FFriction;
  776. if fAir < 0 then
  777. fAir := 0;
  778. if FUseGravity and (not FInGround) then
  779. ScaleVector(FAccel, 0.01);
  780. // v = TIME * force + max(1-TIME*Friction,0) * v;
  781. Accel(FSpeed, fGround, FAccel);
  782. Accel(FAbsSpeed, fGround, FAbsAccel);
  783. (* FSpeed[0] := deltaTime * FAccel[0] + fGround * FSpeed[0];
  784. FSpeed[1] := deltaTime * FAccel[1] + fGround * FSpeed[1];
  785. FSpeed[2] := deltaTime * FAccel[2] + fGround * FSpeed[2];
  786. FAbsSpeed[0] := deltaTime * FAbsAccel[0] + fGround * FAbsSpeed[0];
  787. FAbsSpeed[1] := deltaTime * FAbsAccel[1] + fGround * FAbsSpeed[1];
  788. FAbsSpeed[2] := deltaTime * FAbsAccel[2] + fGround * FAbsSpeed[2]; *)
  789. if FUseGravity then
  790. begin
  791. // Calculate gravity acceleration
  792. if FInGround then
  793. G := FManager.Gravity * abs(1 - VectorDotProduct(FGroundNormal,
  794. FManager.WorldDirection.AsAffineVector))
  795. else
  796. G := FManager.Gravity;
  797. if FJumping then
  798. G := 0;
  799. v := VectorScale(FManager.WorldDirection.AsAffineVector, G);
  800. Accel(FGravSpeed, fAir, v);
  801. (* FGravSpeed[0] := deltaTime * v[0] + fAir * FGravSpeed[0];
  802. FGravSpeed[1] := deltaTime * v[1] + fAir * FGravSpeed[1];
  803. FGravSpeed[2] := deltaTime * v[2] + fAir * FGravSpeed[2]; *)
  804. end
  805. else
  806. FGravSpeed := NullVector;
  807. if FJumping then
  808. begin
  809. FJumpSpeed := FJumpForce;
  810. FJumpHeight := FJumpHeight - (FJumpSpeed * deltaTime);
  811. FJumping := FJumpHeight > 0;
  812. if FJumping then
  813. FGravSpeed := NullVector
  814. else
  815. begin
  816. v := VectorScale(FManager.WorldDirection.AsAffineVector, FJumpSpeed);
  817. AddVector(FGravSpeed, v);
  818. FJumpForce := 0;
  819. FJumpSpeed := 0;
  820. end;
  821. end;
  822. // s = s0 + vt (add relative speed)
  823. if FSlideOrBounce = csbBounce then
  824. deltaS := FSpeed
  825. else
  826. deltaS := RotateVectorByObject(OwnerBaseSceneObject, FSpeed);
  827. // Add absolute speed
  828. AddVector(deltaS, FAbsSpeed);
  829. // Add jump speed
  830. v := VectorScale(FManager.WorldDirection.AsAffineVector, FJumpSpeed);
  831. AddVector(deltaS, v);
  832. // The absolute space must be only the gravity so it can calculate when it is in the ground
  833. deltaAbsS := FGravSpeed;
  834. ScaleVector(deltaS, deltaTime);
  835. ScaleVector(deltaAbsS, deltaTime);
  836. // Returns the friction of all collided objects
  837. FTotalFriction := FManager.MoveByDistance(Self, deltaS, deltaAbsS);
  838. FAccel := NullVector;
  839. FAbsAccel := NullVector;
  840. end;
  841. procedure TgxDCEDynamic.DoProgress(const progressTime: TgxProgressTimes);
  842. begin
  843. inherited DoProgress(progressTime);
  844. Assert(Assigned(Manager), 'DCE Manager not assigned to behaviour.');
  845. if (not FManager.ManualStep) and FActive then
  846. begin
  847. if progressTime.deltaTime > 0.1 then
  848. DoMove(0.1)
  849. else
  850. DoMove(progressTime.deltaTime);
  851. end;
  852. end;
  853. class function TgxDCEDynamic.FriendlyDescription: String;
  854. begin
  855. Result := 'Dynamic Collision-detection registration';
  856. end;
  857. class function TgxDCEDynamic.FriendlyName: String;
  858. begin
  859. Result := 'DCE Dynamic Collider';
  860. end;
  861. procedure TgxDCEDynamic.Jump(jHeight, jSpeed: single);
  862. begin
  863. if (not FJumping) and (FInGround) and
  864. (VectorDotProduct(FGroundNormal, FManager.WorldDirection.AsAffineVector)
  865. > 0.5) then
  866. begin
  867. FJumpHeight := jHeight;
  868. FJumpForce := jSpeed;
  869. FJumpSpeed := FJumpForce;
  870. FJumping := True;
  871. FInGround := False;
  872. AddVector(FAbsSpeed, RotateVectorByObject(OwnerBaseSceneObject, FSpeed));
  873. FSpeed := NullVector;
  874. end;
  875. end;
  876. procedure TgxDCEDynamic.Loaded;
  877. var
  878. mng: TComponent;
  879. begin
  880. inherited;
  881. if FManagerName <> '' then
  882. begin
  883. mng := FindManager(TgxDCEManager, FManagerName);
  884. if Assigned(mng) then
  885. Manager := TgxDCEManager(mng);
  886. FManagerName := '';
  887. end;
  888. end;
  889. procedure TgxDCEDynamic.Move(deltaS: TAffineVector; deltaTime: Double);
  890. begin
  891. ScaleVector(deltaS, deltaTime);
  892. FManager.MoveByDistance(Self, NullVector, deltaS);
  893. end;
  894. procedure TgxDCEDynamic.MoveTo(Position: TAffineVector; Amount: single);
  895. begin
  896. SubtractVector(Position,
  897. AffineVectorMake(OwnerBaseSceneObject.AbsolutePosition));
  898. Move(Position, Amount);
  899. end;
  900. procedure TgxDCEDynamic.WriteToFiler(writer: TWriter);
  901. begin
  902. with writer do
  903. begin
  904. // ArchiveVersion 1, added inherited call
  905. WriteInteger(1);
  906. inherited;
  907. if Assigned(FManager) then
  908. WriteString(FManager.GetNamePath)
  909. else
  910. WriteString('');
  911. WriteInteger(FLayer);
  912. WriteBoolean(FSolid);
  913. WriteBoolean(FActive);
  914. WriteBoolean(FUseGravity);
  915. WriteSingle(FFriction);
  916. WriteSingle(FBounceFactor);
  917. WriteInteger(FMaxRecursionDepth);
  918. WriteInteger(ord(FSlideOrBounce));
  919. FSize.WriteToFiler(writer);
  920. end;
  921. end;
  922. procedure TgxDCEDynamic.ReadFromFiler(reader: TReader);
  923. var
  924. archiveVersion: Integer;
  925. begin
  926. with reader do
  927. begin
  928. archiveVersion := ReadInteger;
  929. Assert(archiveVersion in [0 .. 1]);
  930. if archiveVersion >= 1 then
  931. inherited;
  932. FManagerName := ReadString;
  933. Manager := nil;
  934. FLayer := ReadInteger;
  935. FSolid := ReadBoolean;
  936. FActive := ReadBoolean;
  937. FUseGravity := ReadBoolean;
  938. FFriction := ReadSingle;
  939. FBounceFactor := ReadSingle;
  940. FMaxRecursionDepth := ReadInteger;
  941. FSlideOrBounce := TgxDCESlideOrBounce(ReadInteger);
  942. FSize.ReadFromFiler(reader);
  943. end;
  944. end;
  945. procedure TgxDCEDynamic.SetBounceFactor(const Value: single);
  946. begin
  947. FBounceFactor := Value;
  948. if FBounceFactor < 0 then
  949. FBounceFactor := 0;
  950. if FBounceFactor > 1 then
  951. FBounceFactor := 1;
  952. end;
  953. procedure TgxDCEDynamic.SetFriction(const Value: single);
  954. begin
  955. FFriction := Value;
  956. if FFriction < 0 then
  957. FFriction := 0;
  958. if FFriction > 100 then
  959. FFriction := 100;
  960. end;
  961. procedure TgxDCEDynamic.SetManager(const val: TgxDCEManager);
  962. begin
  963. if val <> FManager then
  964. begin
  965. if Assigned(FManager) then
  966. FManager.DeRegisterDynamic(Self);
  967. if Assigned(val) then
  968. val.RegisterDynamic(Self);
  969. end;
  970. end;
  971. procedure TgxDCEDynamic.SetSize(const Value: TgxCoordinates);
  972. begin
  973. FSize.Assign(Value);
  974. if FSize.x <= 0 then
  975. FSize.x := 0.1;
  976. if FSize.y <= 0 then
  977. FSize.y := 0.1;
  978. if FSize.z <= 0 then
  979. FSize.z := 0.1;
  980. end;
  981. // ----------------------------------------------------------------
  982. function GetOrCreateDCEStatic(behaviours: TgxBehaviours): TgxDCEStatic;
  983. var
  984. i: Integer;
  985. begin
  986. i := behaviours.IndexOfClass(TgxDCEStatic);
  987. if i >= 0 then
  988. result := TgxDCEStatic(behaviours[i])
  989. else
  990. result := TgxDCEStatic.Create(behaviours);
  991. end;
  992. function GetOrCreateDCEStatic(obj: TgxBaseSceneObject): TgxDCEStatic;
  993. begin
  994. result := GetOrCreateDCEStatic(obj.behaviours);
  995. end;
  996. function GetOrCreateDCEDynamic(behaviours: TgxBehaviours): TgxDCEDynamic;
  997. var
  998. i: Integer;
  999. begin
  1000. i := behaviours.IndexOfClass(TgxDCEDynamic);
  1001. if i >= 0 then
  1002. result := TgxDCEDynamic(behaviours[i])
  1003. else
  1004. result := TgxDCEDynamic.Create(behaviours);
  1005. end;
  1006. function GetOrCreateDCEDynamic(obj: TgxBaseSceneObject): TgxDCEDynamic;
  1007. begin
  1008. result := GetOrCreateDCEDynamic(obj.behaviours);
  1009. end;
  1010. // ------------------------------------------------------------------
  1011. initialization
  1012. // ------------------------------------------------------------------
  1013. RegisterXCollectionItemClass(TgxDCEStatic);
  1014. RegisterXCollectionItemClass(TgxDCEDynamic);
  1015. finalization
  1016. UnregisterXCollectionItemClass(TgxDCEStatic);
  1017. UnregisterXCollectionItemClass(TgxDCEDynamic);
  1018. end.