GLX.Ragdoll.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. //
  2. // The graphics platform GLXcene https://github.com/glscene
  3. //
  4. unit GLX.Ragdoll;
  5. (* Base abstract ragdoll class. Should be extended to use any physics system *)
  6. interface
  7. uses
  8. Scena.VectorTypes,
  9. GLX.PersistentClasses,
  10. Scena.VectorGeometry,
  11. GLX.VectorLists,
  12. GLX.Scene,
  13. GLX.Objects,
  14. GLX.VectorFileObjects;
  15. type
  16. TgxRagdoll = class;
  17. TgxRagdolBone = class;
  18. TgxRagdolJoint = class
  19. end;
  20. TgxRagdolBoneList = class (TgxPersistentObjectList)
  21. private
  22. FRagdoll : TgxRagdoll;
  23. protected
  24. function GetRagdollBone(Index: Integer) : TgxRagdolBone;
  25. public
  26. constructor Create(Ragdoll: TgxRagdoll); reintroduce;
  27. destructor Destroy; override;
  28. procedure WriteToFiler(writer : TgxVirtualWriter); override;
  29. procedure ReadFromFiler(reader : TgxVirtualReader); override;
  30. property Ragdoll : TgxRagdoll read FRagdoll;
  31. property Items[Index: Integer] : TgxRagdolBone read GetRagdollBone; default;
  32. end;
  33. TgxRagdolBone = class (TgxRagdolBoneList)
  34. private
  35. FOwner : TgxRagdolBoneList;
  36. FName : String;
  37. FBoneID : Integer; //Refering to TgxActor Bone
  38. FBoundMax: TAffineVector;
  39. FBoundMin: TAffineVector;
  40. FBoundBoneDelta: TAffineVector; //Stores the diference from the bone.GlobalMatrix to the center of the bone's bounding box
  41. FOrigin: TAffineVector;
  42. FSize: TAffineVector;
  43. FBoneMatrix: TMatrix4f;
  44. FJoint: TgxRagdolJoint;
  45. FOriginalMatrix: TMatrix4f; //Stores the Bone.GlobalMatrix before the ragdoll start
  46. FReferenceMatrix: TMatrix4f; //Stores the first bone matrix to be used as reference
  47. FAnchor: TAffineVector; //The position of the joint
  48. procedure CreateBoundingBox;
  49. procedure SetAnchor(Anchor: TAffineVector);
  50. procedure AlignToSkeleton;
  51. procedure CreateBoundsChild;
  52. procedure StartChild;
  53. procedure AlignChild;
  54. procedure UpdateChild;
  55. procedure StopChild;
  56. protected
  57. function GetRagdollBone(Index: Integer) : TgxRagdolBone;
  58. procedure Start; virtual; abstract;
  59. procedure Align; virtual; abstract;
  60. procedure Update; virtual; abstract;
  61. procedure Stop; virtual; abstract;
  62. public
  63. constructor CreateOwned(aOwner : TgxRagdolBoneList);
  64. constructor Create(Ragdoll: TgxRagdoll);
  65. destructor Destroy; override;
  66. procedure WriteToFiler(writer : TgxVirtualWriter); override;
  67. procedure ReadFromFiler(reader : TgxVirtualReader); override;
  68. property Owner : TgxRagdolBoneList read FOwner;
  69. property Name : String read FName write FName;
  70. property BoneID : Integer read FBoneID write FBoneID;
  71. property Origin : TAffineVector read FOrigin;
  72. property Size : TAffineVector read FSize;
  73. property BoneMatrix : TMatrix4f read FBoneMatrix;
  74. property ReferenceMatrix : TMatrix4f read FReferenceMatrix;
  75. property Anchor : TAffineVector read FAnchor;
  76. property Joint : TgxRagdolJoint read FJoint write FJoint;
  77. property Items[Index: Integer] : TgxRagdolBone read GetRagdollBone; default;
  78. end;
  79. TgxRagdoll = class(TgxPersistentObject)
  80. private
  81. FOwner : TgxBaseMesh;
  82. FRootBone : TgxRagdolBone;
  83. FEnabled: Boolean;
  84. FBuilt: Boolean;
  85. public
  86. constructor Create(AOwner : TgxBaseMesh); reintroduce;
  87. destructor Destroy; override;
  88. procedure WriteToFiler(writer : TgxVirtualWriter); override;
  89. procedure ReadFromFiler(reader : TgxVirtualReader); override;
  90. // Must be set before build the ragdoll
  91. procedure SetRootBone(RootBone: TgxRagdolBone);
  92. // Create the bounding box and setup the ragdoll do be started later
  93. procedure BuildRagdoll;
  94. procedure Start;
  95. procedure Update;
  96. procedure Stop;
  97. property Owner : TgxBaseMesh read FOwner;
  98. property RootBone : TgxRagdolBone read FRootBone;
  99. property Enabled : Boolean read FEnabled;
  100. end;
  101. //-----------------------------------------------------------------------
  102. implementation
  103. //-----------------------------------------------------------------------
  104. //--------------------------------
  105. // TgxRagdolBoneList
  106. //--------------------------------
  107. constructor TgxRagdolBoneList.Create(Ragdoll: TgxRagdoll);
  108. begin
  109. inherited Create;
  110. FRagdoll := Ragdoll;
  111. end;
  112. destructor TgxRagdolBoneList.Destroy;
  113. var i: integer;
  114. begin
  115. for i:=0 to Count-1 do Items[i].Destroy;
  116. inherited;
  117. end;
  118. function TgxRagdolBoneList.GetRagdollBone(Index: Integer): TgxRagdolBone;
  119. begin
  120. Result:=TgxRagdolBone(List^[Index]);
  121. end;
  122. procedure TgxRagdolBoneList.ReadFromFiler(reader: TgxVirtualReader);
  123. begin
  124. inherited;
  125. //Not implemented
  126. end;
  127. procedure TgxRagdolBoneList.WriteToFiler(writer: TgxVirtualWriter);
  128. begin
  129. inherited;
  130. //Not implemented
  131. end;
  132. { TgxRagdolBone }
  133. constructor TgxRagdolBone.Create(Ragdoll: TgxRagdoll);
  134. begin
  135. inherited Create(Ragdoll);
  136. end;
  137. procedure TgxRagdolBone.CreateBoundingBox;
  138. var
  139. bone: TgxSkeletonBone;
  140. i, j: integer;
  141. BoneVertices : TgxAffineVectorList;
  142. BoneVertex, max,min: TAffineVector;
  143. invMat, mat: TMatrix4f;
  144. begin
  145. bone := Ragdoll.Owner.Skeleton.BoneByID(FBoneID);
  146. //Get all vertices weighted to this bone
  147. BoneVertices:=TgxAffineVectorList.Create;
  148. for i:=0 to Ragdoll.Owner.MeshObjects.Count-1 do
  149. with TgxSkeletonMeshObject(Ragdoll.Owner.MeshObjects[i]) do
  150. for j:=0 to Vertices.Count-1 do
  151. if bone.BoneID = VerticesBonesWeights[j][0].BoneID then
  152. BoneVertices.FindOrAdd(Vertices[j]);
  153. invMat := bone.GlobalMatrix;
  154. InvertMatrix(invMat);
  155. //For each vertex, get the max and min XYZ (Bounding box)
  156. if BoneVertices.Count > 0 then
  157. begin
  158. BoneVertex := VectorTransform(BoneVertices[0], invMat);
  159. max := BoneVertex;
  160. min := BoneVertex;
  161. for i:=1 to BoneVertices.Count-1 do begin
  162. BoneVertex := VectorTransform(BoneVertices[i], invMat);
  163. if (BoneVertex.X > max.X) then max.X := BoneVertex.X;
  164. if (BoneVertex.Y > max.Y) then max.Y := BoneVertex.Y;
  165. if (BoneVertex.Z > max.Z) then max.Z := BoneVertex.Z;
  166. if (BoneVertex.X < min.X) then min.X := BoneVertex.X;
  167. if (BoneVertex.Y < min.Y) then min.Y := BoneVertex.Y;
  168. if (BoneVertex.Z < min.Z) then min.Z := BoneVertex.Z;
  169. end;
  170. FBoundMax := max;
  171. FBoundMin := min;
  172. //Get the origin and subtract from the bone matrix
  173. FBoundBoneDelta := VectorScale(VectorAdd(FBoundMax, FBoundMin), 0.5);
  174. end else begin
  175. FBoundMax := NullVector;
  176. FBoundMin := NullVector;
  177. end;
  178. AlignToSkeleton;
  179. FReferenceMatrix := FBoneMatrix;
  180. mat := MatrixMultiply(bone.GlobalMatrix,FRagdoll.Owner.AbsoluteMatrix);
  181. //Set Joint position
  182. SetAnchor(AffineVectorMake(mat.W));
  183. BoneVertices.Free; // NEW1
  184. end;
  185. constructor TgxRagdolBone.CreateOwned(aOwner: TgxRagdolBoneList);
  186. begin
  187. Create(aOwner.Ragdoll);
  188. FOwner:=aOwner;
  189. aOwner.Add(Self);
  190. end;
  191. destructor TgxRagdolBone.Destroy;
  192. begin
  193. inherited;
  194. end;
  195. procedure TgxRagdolBone.AlignToSkeleton;
  196. var
  197. o: TAffineVector;
  198. bone: TgxSkeletonBone;
  199. mat, posMat: TMatrix4f;
  200. noBounds: Boolean;
  201. begin
  202. bone := Ragdoll.Owner.Skeleton.BoneByID(FBoneID);
  203. noBounds := VectorIsNull(FBoundMax) and VectorIsNull(FBoundMin);
  204. //Get the bone matrix relative to the Actor matrix
  205. mat := MatrixMultiply(bone.GlobalMatrix,FRagdoll.Owner.AbsoluteMatrix);
  206. //Set Rotation
  207. FBoneMatrix := mat;
  208. NormalizeMatrix(FBoneMatrix);
  209. if (noBounds) then
  210. begin
  211. FOrigin := AffineVectorMake(mat.W);
  212. FSize := AffineVectorMake(0.1,0.1,0.1);
  213. end else begin
  214. //Set Origin
  215. posMat := mat;
  216. posMat.W := NullHmgVector;
  217. o := VectorTransform(FBoundBoneDelta, posMat);
  218. FOrigin := VectorAdd(AffineVectorMake(mat.W), o);
  219. //Set Size
  220. FSize := VectorScale(VectorSubtract(FBoundMax, FBoundMin),0.9);
  221. FSize.X := FSize.X*VectorLength(mat.X);
  222. FSize.Y := FSize.Y*VectorLength(mat.Y);
  223. FSize.Z := FSize.Z*VectorLength(mat.Z);
  224. end;
  225. //Put the origin in the BoneMatrix
  226. FBoneMatrix.W := VectorMake(FOrigin,1);
  227. end;
  228. function TgxRagdolBone.GetRagdollBone(Index: Integer): TgxRagdolBone;
  229. begin
  230. Result:=TgxRagdolBone(List^[Index]);
  231. end;
  232. procedure TgxRagdolBone.ReadFromFiler(reader: TgxVirtualReader);
  233. begin
  234. inherited;
  235. end;
  236. procedure TgxRagdolBone.StartChild;
  237. var i: integer;
  238. begin
  239. FOriginalMatrix := Ragdoll.Owner.Skeleton.BoneByID(FBoneID).GlobalMatrix;
  240. AlignToSkeleton;
  241. Start;
  242. for i := 0 to Count-1 do items[i].StartChild;
  243. end;
  244. procedure TgxRagdolBone.UpdateChild;
  245. var i: integer;
  246. begin
  247. Update;
  248. for i := 0 to Count-1 do items[i].UpdateChild;
  249. end;
  250. procedure TgxRagdolBone.WriteToFiler(writer: TgxVirtualWriter);
  251. begin
  252. inherited;
  253. end;
  254. procedure TgxRagdolBone.StopChild;
  255. var i: integer;
  256. begin
  257. Stop;
  258. Ragdoll.Owner.Skeleton.BoneByID(FBoneID).SetGlobalMatrix(FOriginalMatrix);
  259. for i := 0 to Count-1 do items[i].StopChild;
  260. end;
  261. procedure TgxRagdolBone.CreateBoundsChild;
  262. var i: integer;
  263. begin
  264. CreateBoundingBox;
  265. for i := 0 to Count-1 do items[i].CreateBoundsChild;
  266. end;
  267. procedure TgxRagdolBone.SetAnchor(Anchor: TAffineVector);
  268. begin
  269. FAnchor := Anchor;
  270. end;
  271. procedure TgxRagdolBone.AlignChild;
  272. var i: integer;
  273. begin
  274. Align;
  275. Update;
  276. for i := 0 to Count-1 do items[i].AlignChild;
  277. end;
  278. { TgxRagdoll }
  279. constructor TgxRagdoll.Create(AOwner : TgxBaseMesh);
  280. begin
  281. FOwner := AOwner;
  282. FEnabled := False;
  283. FBuilt := False;
  284. end;
  285. destructor TgxRagdoll.Destroy;
  286. begin
  287. if FEnabled then Stop;
  288. inherited Destroy;
  289. end;
  290. procedure TgxRagdoll.ReadFromFiler(reader: TgxVirtualReader);
  291. begin
  292. inherited;
  293. end;
  294. procedure TgxRagdoll.SetRootBone(RootBone: TgxRagdolBone);
  295. begin
  296. FRootBone := RootBone;
  297. end;
  298. procedure TgxRagdoll.Start;
  299. begin
  300. Assert(FBuilt, 'First you need to build the ragdoll. BuildRagdoll;');
  301. if (FEnabled) then Exit;
  302. FEnabled:= True;
  303. //First start the ragdoll in the reference position
  304. RootBone.StartChild;
  305. //Now align it to the animation
  306. RootBone.AlignChild;
  307. //Now it recalculate the vertices to use as reference
  308. FOwner.Skeleton.StartRagDoll;
  309. end;
  310. procedure TgxRagdoll.Update;
  311. begin
  312. if FEnabled then
  313. begin
  314. RootBone.UpdateChild;
  315. FOwner.Skeleton.MorphMesh(true);
  316. end;
  317. end;
  318. procedure TgxRagdoll.Stop;
  319. begin
  320. if not FEnabled then Exit;
  321. FEnabled := False;
  322. RootBone.StopChild;
  323. //Restore the old information
  324. FOwner.Skeleton.StopRagDoll;
  325. FOwner.Skeleton.MorphMesh(true);
  326. end;
  327. procedure TgxRagdoll.WriteToFiler(writer: TgxVirtualWriter);
  328. begin
  329. inherited;
  330. end;
  331. procedure TgxRagdoll.BuildRagdoll;
  332. begin
  333. Assert(RootBone <> nil, 'First you need to set the root bone. SetRootBone();');
  334. RootBone.CreateBoundsChild;
  335. FBuilt := True;
  336. end;
  337. end.