fdQuakeActor.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. unit fdQuakeActor;
  2. interface
  3. uses
  4. Winapi.OpenGL,
  5. System.SysUtils,
  6. System.Classes,
  7. System.Math,
  8. Vcl.Forms,
  9. Vcl.ExtCtrls,
  10. Vcl.ComCtrls,
  11. Vcl.Controls,
  12. Vcl.StdCtrls,
  13. Vcl.Imaging.JPeg,
  14. Vcl.Dialogs,
  15. GLS.Cadencer,
  16. GLS.Scene,
  17. GLS.Objects,
  18. Stage.VectorTypes,
  19. GLS.SceneViewer,
  20. GLS.VectorFileObjects,
  21. GLS.PersistentClasses,
  22. GLS.ShadowPlane,
  23. Stage.VectorGeometry,
  24. GLS.Texture,
  25. GLS.ParticleFX,
  26. GLS.Material,
  27. GLS.Coordinates,
  28. GLS.BaseClasses,
  29. GLS.FileMD3, // MD3 loading into GLScene
  30. GLS.FileQ3MD3; // Quake3 helper structures and functions
  31. type
  32. TFormQuakeActor = class(TForm)
  33. GLScene1: TGLScene;
  34. GLCamera1: TGLCamera;
  35. GLLightSource1: TGLLightSource;
  36. DummyCube1: TGLDummyCube;
  37. GLCadencer1: TGLCadencer;
  38. Timer1: TTimer;
  39. ModelCube: TGLDummyCube;
  40. MatLib: TGLMaterialLibrary;
  41. Panel1: TPanel;
  42. GLSceneViewer1: TGLSceneViewer;
  43. ComboBox1: TComboBox;
  44. ComboBox2: TComboBox;
  45. Label1: TLabel;
  46. Label2: TLabel;
  47. GLShadowPlane1: TGLShadowPlane;
  48. Legs: TGLActor;
  49. Torso: TGLActor;
  50. Head: TGLActor;
  51. Weapon: TGLActor;
  52. GunSmoke: TGLDummyCube;
  53. GLParticleFXRenderer1: TGLParticleFXRenderer;
  54. GLPointLightPFXManager1: TGLPointLightPFXManager;
  55. TrackBar1: TTrackBar;
  56. TrackBar2: TTrackBar;
  57. Label3: TLabel;
  58. TrackBar3: TTrackBar;
  59. TrackBar4: TTrackBar;
  60. Label4: TLabel;
  61. ComboSkin: TComboBox;
  62. Label5: TLabel;
  63. procedure GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton;
  64. Shift: TShiftState; X, Y: Integer);
  65. procedure GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState;
  66. X, Y: Integer);
  67. procedure Timer1Timer(Sender: TObject);
  68. procedure GLCadencer1Progress(Sender: TObject;
  69. const deltaTime, newTime: Double);
  70. procedure FormCreate(Sender: TObject);
  71. procedure ComboBox1Change(Sender: TObject);
  72. procedure ComboBox2Change(Sender: TObject);
  73. procedure FormDestroy(Sender: TObject);
  74. procedure ComboSkinChange(Sender: TObject);
  75. private
  76. mx, my: Integer;
  77. procedure LoadSkin(SkinFilePath, SkinShortName: string;
  78. Actor1, Actor2, Actor3: TGLActor; GraphicFileExt: string);
  79. public
  80. LegsTags, TorsoTags, WeaponTags: TMD3TagList;
  81. procedure BuildModel;
  82. function InterpolateMatrix(m1, m2: TGLMatrix; delta: single): TGLMatrix;
  83. end;
  84. var
  85. FormQuakeActor: TFormQuakeActor;
  86. i: Integer;
  87. implementation //=============================================================
  88. {$R *.DFM}
  89. //----------------------------------------------------------------------------
  90. procedure TFormQuakeActor.FormCreate(Sender: TObject);
  91. begin
  92. // Build the model
  93. BuildModel;
  94. ModelCube.Scale.SetVector(0.044, 0.044, 0.044);
  95. Legs.AnimationMode := aamLoop;
  96. Torso.AnimationMode := aamLoop;
  97. // Populate the combo boxes with the names of the
  98. // loaded animations
  99. Legs.Animations.SetToStrings(ComboBox1.Items);
  100. Torso.Animations.SetToStrings(ComboBox2.Items);
  101. // Set up some initial animations
  102. ComboBox1.ItemIndex := ComboBox1.Items.IndexOf('LEGS_IDLE');
  103. ComboBox2.ItemIndex := ComboBox2.Items.IndexOf('TORSO_STAND');
  104. // And trigger them
  105. ComboBox1Change(nil);
  106. ComboBox2Change(nil);
  107. end;
  108. // In the MaterialLibrary associated with an actor, after the MD3 actor is loaded, we find
  109. // the Materials associated to each FaceGroup of the meshobjects of the Actor.
  110. // So we parse these Materials and if we find a corrispondence in the SkinFile, we load
  111. // the corresponding JPG in the Material
  112. procedure TFormQuakeActor.LoadSkin(SkinFilePath, SkinShortName: string;
  113. // Ex: "default" or "red" or "blue"
  114. Actor1, Actor2, Actor3: TGLActor; GraphicFileExt: string { Ex : ".JPG" } );
  115. var
  116. Idx: Integer;
  117. MatName: string;
  118. stl, stlBuf, stlPics: TStringList;
  119. MatLib: TGLMaterialLibrary;
  120. PicFileName: string;
  121. procedure FetchStlBuf(Prefix: string);
  122. var
  123. stFileName: string;
  124. begin
  125. stFileName := SkinFilePath + Prefix + SkinShortName;
  126. if FileExists(stFileName) then
  127. stl.LoadFromFile(stFileName);
  128. stlBuf.AddStrings(stl);
  129. end;
  130. function GetMaterialPicFilename(MatName: string): string;
  131. var
  132. n: Integer;
  133. begin
  134. MatName := Uppercase(MatName);
  135. for n := 0 to stlBuf.Count - 1 do
  136. begin
  137. if pos(MatName, Uppercase(stlBuf[n])) = 1 then
  138. begin
  139. Result := ExtractFileName(StringReplace(stlBuf[n], '/', '\',
  140. [rfReplaceAll]));
  141. Break;
  142. end;
  143. end;
  144. end;
  145. procedure DoActorMaterials(Actor: TGLActor);
  146. var
  147. t, u: Integer;
  148. begin
  149. for t := 0 to Actor.MeshObjects.Count - 1 do
  150. begin
  151. for u := 0 to Actor.MeshObjects[t].FaceGroups.Count - 1 do
  152. begin
  153. MatName := Actor.MeshObjects[t].FaceGroups[u].MaterialName;
  154. PicFileName := GetMaterialPicFilename(MatName);
  155. Idx := stlPics.IndexOf(PicFileName);
  156. if Idx = -1 then
  157. begin
  158. stlPics.AddObject(PicFileName, Actor.MeshObjects[t].FaceGroups[u]);
  159. PicFileName := SkinFilePath + ChangeFileExt(PicFileName,
  160. GraphicFileExt);
  161. if FileExists(PicFileName) then
  162. MatLib.Materials.GetLibMaterialByName(MatName)
  163. .Material.Texture.Image.LoadFromFile(PicFileName);
  164. end
  165. else
  166. begin
  167. Actor.MeshObjects[t].FaceGroups[u].MaterialName :=
  168. TGLFaceGroup(stlPics.Objects[Idx]).MaterialName;
  169. end;
  170. end;
  171. end;
  172. end;
  173. begin
  174. MatLib := Actor1.MaterialLibrary;
  175. if MatLib = nil then
  176. Exit;
  177. stl := TStringList.create;
  178. stlBuf := TStringList.create;
  179. stlPics := TStringList.create;
  180. SkinFilePath := IncludeTrailingBackslash(SkinFilePath);
  181. SkinShortName := ChangeFileExt(SkinShortName, '.skin');
  182. FetchStlBuf('Head_');
  183. FetchStlBuf('Upper_');
  184. FetchStlBuf('Lower_');
  185. DoActorMaterials(Actor1);
  186. DoActorMaterials(Actor2);
  187. DoActorMaterials(Actor3);
  188. stl.Free;
  189. stlBuf.Free;
  190. stlPics.Free;
  191. end;
  192. //----------------------------------------------------------------------------
  193. procedure TFormQuakeActor.BuildModel;
  194. // var
  195. // t: integer;
  196. begin
  197. // Load model data from MD3 files into the actor
  198. //
  199. Legs.LoadFromFile('.\Model\lower.md3');
  200. Torso.LoadFromFile('.\Model\upper.md3');
  201. Head.LoadFromFile('.\Model\head.md3');
  202. Weapon.LoadFromFile('.\Model\plasma.md3');
  203. // Load the required tag lists
  204. // These are used to loacally transform the separate
  205. // parts of the model into the correct places
  206. //
  207. LegsTags := TMD3TagList.Create;
  208. LegsTags.LoadFromFile('.\model\lower.md3');
  209. TorsoTags := TMD3TagList.Create;
  210. TorsoTags.LoadFromFile('.\model\upper.md3');
  211. // The tag_flash tag in the railgun model gives the
  212. // transform offset for the nozzle of the gun. I've
  213. // added a GunSmoke dummycube there to demonstrate with
  214. // a smoke like effect
  215. //
  216. WeaponTags := TMD3TagList.Create;
  217. WeaponTags.LoadFromFile('.\model\plasma.md3');
  218. GunSmoke.Matrix^ := WeaponTags.GetTransform('tag_flash', 0);
  219. // Apply textures to preloaded materials
  220. // The md3 file loader puts a material into the actors
  221. // assigned material library (if there is one) with
  222. // the names of the mesh objects. The skin and/or shader
  223. // files can tell you which objects need which textures
  224. // loaded
  225. //
  226. // Mrqzzz's quick procedure for loading skins
  227. LoadSkin('.\model\', 'default', Head, Torso, Legs, '.jpg');
  228. // Alternative method
  229. // LoadQ3Skin('.\model\lower_default.skin',Legs);
  230. // LoadQ3Skin('.\model\upper_default.skin',Torso);
  231. // LoadQ3Skin('.\model\head_default.skin',Head);
  232. // Load the weapon textures
  233. //
  234. with MatLib.Materials do
  235. begin
  236. with GetLibMaterialByName('plasma2').Material.Texture do
  237. Image.LoadFromFile('.\model\plasma2.jpg');
  238. end;
  239. // Load the animation data from the cfg file
  240. // This procedure populates an animation list from a
  241. // file or TStrings object. The last parameter tells
  242. // it which class of animation is to be loaded.
  243. //
  244. LoadQ3Anims(Legs.Animations, '.\model\animation.cfg', 'BOTH');
  245. LoadQ3Anims(Legs.Animations, '.\model\animation.cfg', 'LEGS');
  246. LoadQ3Anims(Torso.Animations, '.\model\animation.cfg', 'BOTH');
  247. LoadQ3Anims(Torso.Animations, '.\model\animation.cfg', 'TORSO');
  248. end;
  249. //----------------------------------------------------------------------------
  250. function TFormQuakeActor.InterpolateMatrix(m1, m2: TGLMatrix; delta: single): TGLMatrix;
  251. var
  252. i, j: Integer;
  253. begin
  254. // This is used for interpolating between 2 matrices. The result
  255. // is used to reposition the model parts each frame.
  256. //
  257. for j := 0 to 3 do
  258. for i := 0 to 3 do
  259. Result.V[i].V[j] := m1.V[i].V[j] + (m2.V[i].V[j] - m1.V[i].V[j]) * delta;
  260. end;
  261. //----------------------------------------------------------------------------
  262. procedure TFormQuakeActor.GLCadencer1Progress(Sender: TObject;
  263. const deltaTime, newTime: Double);
  264. var
  265. m1, m2: TGLMatrix;
  266. begin
  267. // Set the transform for the torso
  268. m1 := LegsTags.GetTransform('tag_torso', Legs.CurrentFrame);
  269. m2 := LegsTags.GetTransform('tag_torso', Legs.NextFrameIndex);
  270. Torso.Matrix^ := InterpolateMatrix(m1, m2, Legs.CurrentFrameDelta);
  271. Torso.Roll(-TrackBar1.Position);
  272. Torso.Turn(-TrackBar2.Position);
  273. // Set the transform for the head
  274. m1 := TorsoTags.GetTransform('tag_head', Torso.CurrentFrame);
  275. m2 := TorsoTags.GetTransform('tag_head', Torso.NextFrameIndex);
  276. Head.Matrix^ := InterpolateMatrix(m1, m2, Torso.CurrentFrameDelta);
  277. Head.Roll(-TrackBar3.Position);
  278. Head.Turn(-TrackBar4.Position);
  279. // Set the transform for the weapon
  280. m1 := TorsoTags.GetTransform('tag_weapon', Torso.CurrentFrame);
  281. m2 := TorsoTags.GetTransform('tag_weapon', Torso.NextFrameIndex);
  282. Weapon.Matrix^ := InterpolateMatrix(m1, m2, Torso.CurrentFrameDelta);
  283. GLSceneViewer1.Invalidate;
  284. end;
  285. //----------------------------------------------------------------------------
  286. procedure TFormQuakeActor.ComboBox1Change(Sender: TObject);
  287. begin
  288. Legs.SwitchToAnimation(ComboBox1.Text, False);
  289. end;
  290. //----------------------------------------------------------------------------
  291. procedure TFormQuakeActor.ComboBox2Change(Sender: TObject);
  292. begin
  293. Torso.SwitchToAnimation(ComboBox2.Text, False);
  294. end;
  295. //----------------------------------------------------------------------------
  296. procedure TFormQuakeActor.ComboSkinChange(Sender: TObject);
  297. begin
  298. LoadSkin('.\model\', ComboSkin.Text, Head, Torso, Legs, '.jpg');
  299. end;
  300. //----------------------------------------------------------------------------
  301. procedure TFormQuakeActor.GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton;
  302. Shift: TShiftState; X, Y: Integer);
  303. begin
  304. mx := X;
  305. my := Y;
  306. end;
  307. //----------------------------------------------------------------------------
  308. procedure TFormQuakeActor.GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState;
  309. X, Y: Integer);
  310. begin
  311. if ssLeft in Shift then
  312. GLCamera1.MoveAroundTarget(my - Y, mx - X);
  313. if ssRight in Shift then
  314. GLCamera1.AdjustDistanceToTarget(Power(1.05, my - Y));
  315. mx := X;
  316. my := Y;
  317. end;
  318. //----------------------------------------------------------------------------
  319. procedure TFormQuakeActor.Timer1Timer(Sender: TObject);
  320. begin
  321. Caption := Format('%.1f FPS', [GLSceneViewer1.FramesPerSecond]);
  322. GLSceneViewer1.ResetPerformanceMonitor;
  323. end;
  324. //----------------------------------------------------------------------------
  325. procedure TFormQuakeActor.FormDestroy(Sender: TObject);
  326. begin
  327. LegsTags.Free;
  328. TorsoTags.Free;
  329. WeaponTags.Free;
  330. end;
  331. end.