fQ3Demo.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. unit fQ3Demo;
  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. GLS.VectorTypes,
  19. GLS.SceneViewer,
  20. GLS.VectorFileObjects,
  21. GLS.PersistentClasses,
  22. GLS.ShadowPlane,
  23. GLS.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. TForm1 = 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. Form1: TForm1;
  86. i: Integer;
  87. implementation
  88. {$R *.DFM}
  89. procedure TForm1.FormCreate(Sender: TObject);
  90. begin
  91. // Build the model
  92. BuildModel;
  93. ModelCube.Scale.SetVector(0.044, 0.044, 0.044);
  94. Legs.AnimationMode := aamLoop;
  95. Torso.AnimationMode := aamLoop;
  96. // Populate the combo boxes with the names of the
  97. // loaded animations
  98. Legs.Animations.SetToStrings(ComboBox1.Items);
  99. Torso.Animations.SetToStrings(ComboBox2.Items);
  100. // Set up some initial animations
  101. ComboBox1.ItemIndex := ComboBox1.Items.IndexOf('LEGS_IDLE');
  102. ComboBox2.ItemIndex := ComboBox2.Items.IndexOf('TORSO_STAND');
  103. // And trigger them
  104. ComboBox1Change(nil);
  105. ComboBox2Change(nil);
  106. end;
  107. // In the MaterialLibrary associated with an actor, after the MD3 actor is loaded, we find
  108. // the Materials associated to each FaceGroup of the meshobjects of the Actor.
  109. // So we parse these Materials and if we find a corrispondence in the SkinFile, we load
  110. // the corresponding JPG in the Material
  111. procedure TForm1.LoadSkin(SkinFilePath, SkinShortName: string;
  112. // Ex: "default" or "red" or "blue"
  113. Actor1, Actor2, Actor3: TGLActor; GraphicFileExt: string { Ex : ".JPG" } );
  114. var
  115. Idx: Integer;
  116. MatName: string;
  117. stl, stlBuf, stlPics: TStringList;
  118. MatLib: TGLMaterialLibrary;
  119. PicFileName: string;
  120. procedure FetchStlBuf(Prefix: string);
  121. var
  122. stFileName: string;
  123. begin
  124. stFileName := SkinFilePath + Prefix + SkinShortName;
  125. if FileExists(stFileName) then
  126. stl.LoadFromFile(stFileName);
  127. stlBuf.AddStrings(stl);
  128. end;
  129. function GetMaterialPicFilename(MatName: string): string;
  130. var
  131. n: Integer;
  132. begin
  133. MatName := Uppercase(MatName);
  134. for n := 0 to stlBuf.Count - 1 do
  135. begin
  136. if pos(MatName, Uppercase(stlBuf[n])) = 1 then
  137. begin
  138. Result := ExtractFileName(StringReplace(stlBuf[n], '/', '\',
  139. [rfReplaceAll]));
  140. Break;
  141. end;
  142. end;
  143. end;
  144. procedure DoActorMaterials(Actor: TGLActor);
  145. var
  146. t, u: Integer;
  147. begin
  148. for t := 0 to Actor.MeshObjects.Count - 1 do
  149. begin
  150. for u := 0 to Actor.MeshObjects[t].FaceGroups.Count - 1 do
  151. begin
  152. MatName := Actor.MeshObjects[t].FaceGroups[u].MaterialName;
  153. PicFileName := GetMaterialPicFilename(MatName);
  154. Idx := stlPics.IndexOf(PicFileName);
  155. if Idx = -1 then
  156. begin
  157. stlPics.AddObject(PicFileName, Actor.MeshObjects[t].FaceGroups[u]);
  158. PicFileName := SkinFilePath + ChangeFileExt(PicFileName,
  159. GraphicFileExt);
  160. if FileExists(PicFileName) then
  161. MatLib.Materials.GetLibMaterialByName(MatName)
  162. .Material.Texture.Image.LoadFromFile(PicFileName);
  163. end
  164. else
  165. begin
  166. Actor.MeshObjects[t].FaceGroups[u].MaterialName :=
  167. TGLFaceGroup(stlPics.Objects[Idx]).MaterialName;
  168. end;
  169. end;
  170. end;
  171. end;
  172. begin
  173. MatLib := Actor1.MaterialLibrary;
  174. if MatLib = nil then
  175. Exit;
  176. stl := TStringList.create;
  177. stlBuf := TStringList.create;
  178. stlPics := TStringList.create;
  179. SkinFilePath := IncludeTrailingBackslash(SkinFilePath);
  180. SkinShortName := ChangeFileExt(SkinShortName, '.skin');
  181. FetchStlBuf('Head_');
  182. FetchStlBuf('Upper_');
  183. FetchStlBuf('Lower_');
  184. DoActorMaterials(Actor1);
  185. DoActorMaterials(Actor2);
  186. DoActorMaterials(Actor3);
  187. stl.Free;
  188. stlBuf.Free;
  189. stlPics.Free;
  190. end;
  191. procedure TForm1.BuildModel;
  192. // var
  193. // t: integer;
  194. begin
  195. // Load model data from MD3 files into the actor
  196. //
  197. Legs.LoadFromFile('.\Model\lower.md3');
  198. Torso.LoadFromFile('.\Model\upper.md3');
  199. Head.LoadFromFile('.\Model\head.md3');
  200. Weapon.LoadFromFile('.\Model\plasma.md3');
  201. // Load the required tag lists
  202. // These are used to loacally transform the separate
  203. // parts of the model into the correct places
  204. //
  205. LegsTags := TMD3TagList.Create;
  206. LegsTags.LoadFromFile('.\model\lower.md3');
  207. TorsoTags := TMD3TagList.Create;
  208. TorsoTags.LoadFromFile('.\model\upper.md3');
  209. // The tag_flash tag in the railgun model gives the
  210. // transform offset for the nozzle of the gun. I've
  211. // added a GunSmoke dummycube there to demonstrate with
  212. // a smoke like effect
  213. //
  214. WeaponTags := TMD3TagList.Create;
  215. WeaponTags.LoadFromFile('.\model\plasma.md3');
  216. GunSmoke.Matrix^ := WeaponTags.GetTransform('tag_flash', 0);
  217. // Apply textures to preloaded materials
  218. // The md3 file loader puts a material into the actors
  219. // assigned material library (if there is one) with
  220. // the names of the mesh objects. The skin and/or shader
  221. // files can tell you which objects need which textures
  222. // loaded
  223. //
  224. // Mrqzzz's quick procedure for loading skins
  225. LoadSkin('.\model\', 'default', Head, Torso, Legs, '.jpg');
  226. // Alternative method
  227. // LoadQ3Skin('.\model\lower_default.skin',Legs);
  228. // LoadQ3Skin('.\model\upper_default.skin',Torso);
  229. // LoadQ3Skin('.\model\head_default.skin',Head);
  230. // Load the weapon textures
  231. //
  232. with MatLib.Materials do
  233. begin
  234. with GetLibMaterialByName('plasma2').Material.Texture do
  235. Image.LoadFromFile('.\model\plasma2.jpg');
  236. end;
  237. // Load the animation data from the cfg file
  238. // This procedure populates an animation list from a
  239. // file or TStrings object. The last parameter tells
  240. // it which class of animation is to be loaded.
  241. //
  242. LoadQ3Anims(Legs.Animations, '.\model\animation.cfg', 'BOTH');
  243. LoadQ3Anims(Legs.Animations, '.\model\animation.cfg', 'LEGS');
  244. LoadQ3Anims(Torso.Animations, '.\model\animation.cfg', 'BOTH');
  245. LoadQ3Anims(Torso.Animations, '.\model\animation.cfg', 'TORSO');
  246. end;
  247. function TForm1.InterpolateMatrix(m1, m2: TGLMatrix; delta: single): TGLMatrix;
  248. var
  249. i, j: Integer;
  250. begin
  251. // This is used for interpolating between 2 matrices. The result
  252. // is used to reposition the model parts each frame.
  253. //
  254. for j := 0 to 3 do
  255. for i := 0 to 3 do
  256. Result.V[i].V[j] := m1.V[i].V[j] + (m2.V[i].V[j] - m1.V[i].V[j]) * delta;
  257. end;
  258. procedure TForm1.GLCadencer1Progress(Sender: TObject;
  259. const deltaTime, newTime: Double);
  260. var
  261. m1, m2: TGLMatrix;
  262. begin
  263. // Set the transform for the torso
  264. m1 := LegsTags.GetTransform('tag_torso', Legs.CurrentFrame);
  265. m2 := LegsTags.GetTransform('tag_torso', Legs.NextFrameIndex);
  266. Torso.Matrix^ := InterpolateMatrix(m1, m2, Legs.CurrentFrameDelta);
  267. Torso.Roll(-TrackBar1.Position);
  268. Torso.Turn(-TrackBar2.Position);
  269. // Set the transform for the head
  270. m1 := TorsoTags.GetTransform('tag_head', Torso.CurrentFrame);
  271. m2 := TorsoTags.GetTransform('tag_head', Torso.NextFrameIndex);
  272. Head.Matrix^ := InterpolateMatrix(m1, m2, Torso.CurrentFrameDelta);
  273. Head.Roll(-TrackBar3.Position);
  274. Head.Turn(-TrackBar4.Position);
  275. // Set the transform for the weapon
  276. m1 := TorsoTags.GetTransform('tag_weapon', Torso.CurrentFrame);
  277. m2 := TorsoTags.GetTransform('tag_weapon', Torso.NextFrameIndex);
  278. Weapon.Matrix^ := InterpolateMatrix(m1, m2, Torso.CurrentFrameDelta);
  279. GLSceneViewer1.Invalidate;
  280. end;
  281. procedure TForm1.ComboBox1Change(Sender: TObject);
  282. begin
  283. Legs.SwitchToAnimation(ComboBox1.Text, False);
  284. end;
  285. procedure TForm1.ComboBox2Change(Sender: TObject);
  286. begin
  287. Torso.SwitchToAnimation(ComboBox2.Text, False);
  288. end;
  289. procedure TForm1.ComboSkinChange(Sender: TObject);
  290. begin
  291. LoadSkin('.\model\', ComboSkin.Text, Head, Torso, Legs, '.jpg');
  292. end;
  293. procedure TForm1.GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton;
  294. Shift: TShiftState; X, Y: Integer);
  295. begin
  296. mx := X;
  297. my := Y;
  298. end;
  299. procedure TForm1.GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState;
  300. X, Y: Integer);
  301. begin
  302. if ssLeft in Shift then
  303. GLCamera1.MoveAroundTarget(my - Y, mx - X);
  304. if ssRight in Shift then
  305. GLCamera1.AdjustDistanceToTarget(Power(1.05, my - Y));
  306. mx := X;
  307. my := Y;
  308. end;
  309. procedure TForm1.Timer1Timer(Sender: TObject);
  310. begin
  311. Caption := Format('%.1f FPS', [GLSceneViewer1.FramesPerSecond]);
  312. GLSceneViewer1.ResetPerformanceMonitor;
  313. end;
  314. procedure TForm1.FormDestroy(Sender: TObject);
  315. begin
  316. LegsTags.Free;
  317. TorsoTags.Free;
  318. WeaponTags.Free;
  319. end;
  320. end.