fdClothActor.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. unit fdClothActor;
  2. interface
  3. uses
  4. Winapi.OpenGL,
  5. System.SysUtils,
  6. System.Classes,
  7. Vcl.Graphics,
  8. Vcl.Controls,
  9. Vcl.Forms,
  10. Vcl.Dialogs,
  11. Vcl.StdCtrls,
  12. Vcl.ExtCtrls,
  13. Vcl.Imaging.Jpeg,
  14. GLS.Scene,
  15. Stage.VectorTypes,
  16. GLS.VectorFileObjects,
  17. GLS.Objects,
  18. GLS.Cadencer,
  19. GLS.Texture,
  20. GLS.SceneViewer,
  21. GLS.FileSMD,
  22. GLS.File3DS,
  23. GLS.VerletTypes,
  24. GLS.VerletClothify,
  25. GLS.ShadowVolume,
  26. GLS.XCollection,
  27. Stage.VectorGeometry,
  28. GLS.GeometryBB,
  29. GLS.SpacePartition,
  30. GLS.Material,
  31. GLS.BaseClasses,
  32. GLS.RenderContextInfo,
  33. GLS.Context,
  34. Stage.Utils,
  35. GLS.Coordinates,
  36. GLS.PersistentClasses;
  37. type
  38. TFormClothActor = class(TForm)
  39. GLScene1: TGLScene;
  40. GLSceneViewer1: TGLSceneViewer;
  41. GLMaterialLibrary1: TGLMaterialLibrary;
  42. GLCadencer1: TGLCadencer;
  43. GLCamera1: TGLCamera;
  44. GLActor1: TGLActor;
  45. ActorDummy: TGLDummyCube;
  46. Timer1: TTimer;
  47. OctreeRenderer: TGLDirectOpenGL;
  48. cbShowOctree: TCheckBox;
  49. GLLightSource1: TGLLightSource;
  50. GLPlane1: TGLPlane;
  51. GLShadowVolume1: TGLShadowVolume;
  52. Cape: TGLActor;
  53. GLLightSource2: TGLLightSource;
  54. StaticTextFPS: TStaticText;
  55. procedure FormCreate(Sender: TObject);
  56. procedure FormDestroy(Sender: TObject);
  57. procedure GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
  58. X, Y: Integer);
  59. procedure GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  60. procedure GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double);
  61. procedure Timer1Timer(Sender: TObject);
  62. procedure OctreeRendererRender(Sender: TObject; var rci: TGLRenderContextInfo);
  63. public
  64. mx, my: Integer;
  65. VerletWorld: TGLVerletWorld;
  66. EdgeDetector: TGLEdgeDetector;
  67. AirResistance: TGLVerletAirResistance;
  68. end;
  69. var
  70. FormClothActor: TFormClothActor;
  71. implementation //=============================================================
  72. {$R *.dfm}
  73. // Mesh normal recalculation routines
  74. //----------------------------------------------------------------------------
  75. procedure PrepareMeshForNormalsRecalc(BaseMesh: TGLBaseMesh);
  76. var
  77. i, j, k: Integer;
  78. mo: TGLMeshObject;
  79. fg: TFGVertexNormalTexIndexList;
  80. begin
  81. // update normals
  82. // (not very efficient, could use some work...)
  83. for i := 0 to BaseMesh.MeshObjects.Count - 1 do
  84. begin
  85. mo := BaseMesh.MeshObjects[i];
  86. for j := 0 to mo.FaceGroups.Count - 1 do
  87. begin
  88. if mo.FaceGroups[j] is TFGVertexNormalTexIndexList then
  89. begin
  90. fg := TFGVertexNormalTexIndexList(mo.FaceGroups[j]);
  91. for k := 0 to fg.VertexIndices.Count - 1 do
  92. begin
  93. fg.NormalIndices.List[k] := fg.VertexIndices.List[k];
  94. end;
  95. end;
  96. end;
  97. end;
  98. BaseMesh.StructureChanged;
  99. end;
  100. //----------------------------------------------------------------------------
  101. procedure RecalcMeshNormals(BaseMesh: TGLBaseMesh);
  102. var
  103. i, j, k: Integer;
  104. mo: TGLMeshObject;
  105. fg: TFGVertexIndexList;
  106. n: TAffineVector;
  107. begin
  108. // update normals
  109. // (not very efficient, could use some work...)
  110. for i := 0 to BaseMesh.MeshObjects.Count - 1 do
  111. begin
  112. mo := BaseMesh.MeshObjects[i];
  113. FillChar(mo.Normals.List[0], SizeOf(TAffineVector) * mo.Normals.Count, 0);
  114. for j := 0 to mo.FaceGroups.Count - 1 do
  115. begin
  116. if mo.FaceGroups[j] is TFGVertexIndexList then
  117. begin
  118. fg := TFGVertexIndexList(mo.FaceGroups[j]);
  119. k := 0;
  120. while k <= fg.VertexIndices.Count - 3 do
  121. begin
  122. n := CalcPlaneNormal(mo.Vertices.List[fg.VertexIndices.List[k]],
  123. mo.Vertices.List[fg.VertexIndices.List[k + 1]],
  124. mo.Vertices.List[fg.VertexIndices.List[k + 2]]);
  125. mo.Normals.TranslateItem(fg.VertexIndices.List[k], n);
  126. mo.Normals.TranslateItem(fg.VertexIndices.List[k + 1], n);
  127. mo.Normals.TranslateItem(fg.VertexIndices.List[k + 2], n); // }
  128. Inc(k, 3);
  129. end;
  130. end;
  131. end;
  132. mo.Normals.Normalize;
  133. end;
  134. BaseMesh.StructureChanged;
  135. end;
  136. //----------------------------------------------------------------------------
  137. procedure TFormClothActor.FormCreate(Sender: TObject);
  138. var
  139. FloorVC: TGLVerletFloor;
  140. Path: TFileName;
  141. begin
  142. Path := GetCurrentAssetPath();
  143. Randomize;
  144. // Load dynamic models of actors with textures and/or animations
  145. SetCurrentDir(Path + '\modelext');
  146. GLActor1.LoadFromFile('trinityRAGE.smd');
  147. GLActor1.AddDataFromFile('walk.smd');
  148. GLActor1.Animations[1].MakeSkeletalTranslationStatic;
  149. GLActor1.SwitchToAnimation('walk');
  150. GLActor1.BuildSilhouetteConnectivityData;
  151. // Load static models of objects
  152. SetCurrentDir(Path + '\model');
  153. // the cape must be loaded after trinityRAGE
  154. Cape.LoadFromFile('cape.3ds');
  155. Cape.Position.Y := GLActor1.BoundingSphereRadius - 10;
  156. PrepareMeshForNormalsRecalc(Cape);
  157. Cape.BuildSilhouetteConnectivityData;
  158. // Set up the floor texture and reposition to below the actors feet
  159. SetCurrentDir(Path + '\texture');
  160. GLPlane1.Material.Texture.Image.LoadFromFile('beigemarble.jpg');
  161. GLPlane1.Material.Texture.Disabled := False;
  162. GLPlane1.Position.Y := -GLActor1.BoundingSphereRadius * 0.9;
  163. // Setting up the verlet world using the optional dynamic octree can
  164. // give good perfamnce increases.
  165. VerletWorld := TGLVerletWorld.Create;
  166. VerletWorld.CreateOctree(AffineVectorMake(0, 0, 0), AffineVectorMake(0, 0, 0), 10, 6);
  167. VerletWorld.UpdateSpacePartion := uspEveryFrame;
  168. VerletWorld.Iterations := 3;
  169. // 'Clothify' the cape and add it to the verlet world
  170. EdgeDetector := TGLEdgeDetector.Create(Cape);
  171. EdgeDetector.ProcessMesh;
  172. EdgeDetector.AddEdgesAsSticks(VerletWorld, 0.15);
  173. EdgeDetector.AddEdgesAsSolidEdges(VerletWorld);
  174. // EdgeDetector.AddOuterEdgesAsSolidEdges(VerletWorld);
  175. // Set up verlet gravity and add the floor as a constraint
  176. TGLVerletGravity.Create(VerletWorld).Gravity := AffineVectorMake(0, -98.1, 0);
  177. FloorVC := TGLVerletFloor.Create(VerletWorld);
  178. FloorVC.Normal := GLPlane1.Direction.AsAffineVector;
  179. FloorVC.Location := VectorAdd(GLPlane1.Position.AsAffineVector,
  180. VectorScale(GLPlane1.Direction.AsAffineVector, 0.1));
  181. (* Load the skeleton colliders. Skeleton colliders define an
  182. approximate collision boundary for actors and are controlled
  183. by the actor's skeleton. *)
  184. SetCurrentDir(Path + '\scenery');
  185. GLActor1.Skeleton.Colliders.LoadFromFile('trinityRAGE.glsc');
  186. GLActor1.Skeleton.Colliders.AlignColliders;
  187. // Add the collider's verlet constraints to the verlet world
  188. AddVerletConstriantsToVerletWorld(GLActor1.Skeleton.Colliders, VerletWorld);
  189. (*
  190. AirResistance := TGLVerletAirResistance.Create(VerletWorld);
  191. AirResistance.DragCoeff := 0.001;
  192. AirResistance.WindDirection := AffineVectorMake(0,0,1);
  193. AirResistance.WindMagnitude := 15;
  194. AirResistance.WindChaos := 2;
  195. // *)
  196. FloorVC.Free;
  197. end;
  198. //----------------------------------------------------------------------------
  199. procedure TFormClothActor.FormDestroy(Sender: TObject);
  200. begin
  201. VerletWorld.Free;
  202. end;
  203. procedure TFormClothActor.GLCadencer1Progress(Sender: TObject; const deltaTime, newTime: Double);
  204. var
  205. i: Integer;
  206. begin
  207. // Step the verlet world (this is where the magic happens)
  208. VerletWorld.Progress(deltaTime, newTime);
  209. // Recalculate the cape's normals
  210. RecalcMeshNormals(Cape);
  211. // Cycle the floor texture to make it look like it's moving
  212. GLPlane1.YOffset := GLPlane1.YOffset - 0.25 * deltaTime;
  213. if GLPlane1.YOffset < 0 then
  214. GLPlane1.YOffset := GLPlane1.YOffset + 1;
  215. // Orbit the light (to show off the pretty shadow volumes)
  216. GLLightSource1.MoveObjectAround(GLActor1, 0, -deltaTime * 20);
  217. GLLightSource1.PointTo(GLActor1, YHMGVector);
  218. end;
  219. //----------------------------------------------------------------------------
  220. procedure RenderAABB(AABB: TAABB; w, r, g, b: single);
  221. begin
  222. glColor3f(r, g, b);
  223. glLineWidth(w);
  224. glBegin(GL_LINE_STRIP);
  225. glVertex3f(AABB.min.X, AABB.min.Y, AABB.min.Z);
  226. glVertex3f(AABB.min.X, AABB.max.Y, AABB.min.Z);
  227. glVertex3f(AABB.max.X, AABB.max.Y, AABB.min.Z);
  228. glVertex3f(AABB.max.X, AABB.min.Y, AABB.min.Z);
  229. glVertex3f(AABB.min.X, AABB.min.Y, AABB.min.Z);
  230. glVertex3f(AABB.min.X, AABB.min.Y, AABB.max.Z);
  231. glVertex3f(AABB.min.X, AABB.max.Y, AABB.max.Z);
  232. glVertex3f(AABB.max.X, AABB.max.Y, AABB.max.Z);
  233. glVertex3f(AABB.max.X, AABB.min.Y, AABB.max.Z);
  234. glVertex3f(AABB.min.X, AABB.min.Y, AABB.max.Z);
  235. glEnd;
  236. glBegin(GL_LINES);
  237. glVertex3f(AABB.min.X, AABB.max.Y, AABB.min.Z);
  238. glVertex3f(AABB.min.X, AABB.max.Y, AABB.max.Z);
  239. glVertex3f(AABB.max.X, AABB.max.Y, AABB.min.Z);
  240. glVertex3f(AABB.max.X, AABB.max.Y, AABB.max.Z);
  241. glVertex3f(AABB.max.X, AABB.min.Y, AABB.min.Z);
  242. glVertex3f(AABB.max.X, AABB.min.Y, AABB.max.Z);
  243. glEnd;
  244. end;
  245. //----------------------------------------------------------------------------
  246. procedure RenderOctreeNode(Node: TGLSectorNode);
  247. var
  248. i: Integer;
  249. AABB: TAABB;
  250. begin
  251. if Node.NoChildren then
  252. begin
  253. AABB := Node.AABB;
  254. if Node.RecursiveLeafCount > 0 then
  255. RenderAABB(AABB, 1, 0, 0, 0)
  256. else
  257. RenderAABB(AABB, 1, 0.8, 0.8, 0.8) // }
  258. end
  259. else
  260. begin
  261. for i := 0 to Node.ChildCount - 1 do
  262. RenderOctreeNode(Node.Children[i]);
  263. end;
  264. end;
  265. //----------------------------------------------------------------------------
  266. procedure TFormClothActor.OctreeRendererRender(Sender: TObject; var rci: TGLRenderContextInfo);
  267. begin
  268. if cbShowOctree.Checked then
  269. begin
  270. if VerletWorld.SpacePartition is TGLOctreeSpacePartition then
  271. begin
  272. glPushAttrib(GL_ENABLE_BIT or GL_CURRENT_BIT or GL_LINE_BIT or GL_COLOR_BUFFER_BIT);
  273. glDisable(GL_LIGHTING);
  274. RenderOctreeNode(TGLOctreeSpacePartition(VerletWorld.SpacePartition).RootNode);
  275. glPopAttrib;
  276. end;
  277. end;
  278. end;
  279. //----------------------------------------------------------------------------
  280. procedure TFormClothActor.Timer1Timer(Sender: TObject);
  281. begin
  282. StaticTextFPS.Caption := Format('%2.1f FPS', [GLSceneViewer1.FramesPerSecond]);
  283. GLSceneViewer1.ResetPerformanceMonitor;
  284. end;
  285. procedure TFormClothActor.GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton;
  286. Shift: TShiftState; X, Y: Integer);
  287. begin
  288. mx := X;
  289. my := Y;
  290. end;
  291. procedure TFormClothActor.GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState;
  292. X, Y: Integer);
  293. begin
  294. if ssLeft in Shift then
  295. GLCamera1.MoveAroundTarget(my - Y, mx - X);
  296. mx := X;
  297. my := Y;
  298. end;
  299. end.