GXS.FileMD5.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.FileMD5;
  5. (* Doom3 MD5 mesh and animation vector file format implementation *)
  6. interface
  7. uses
  8. System.Classes,
  9. System.SysUtils,
  10. GXS.VectorFileObjects,
  11. Stage.Utils,
  12. GXS.ApplicationFileIO,
  13. Stage.VectorTypes,
  14. Stage.VectorGeometry,
  15. GXS.VectorLists;
  16. type
  17. TgxMD5VectorFile = class(TgxVectorFile)
  18. private
  19. FMD5String, FTempString, FBoneNames: TStringList;
  20. FCurrentPos: Integer;
  21. FBasePose: TgxSkeletonFrame;
  22. FFramePositions: TgxAffineVectorList;
  23. FFrameQuaternions: TgxQuaternionList;
  24. FJointFlags: TgxIntegerList;
  25. FNumFrames, FFirstFrame, FFrameRate, FNumJoints: Integer;
  26. function ReadLine: String;
  27. public
  28. class function Capabilities: TDataFileCapabilities; override;
  29. procedure LoadFromStream(aStream: TStream); override;
  30. end;
  31. var
  32. vMD5TextureExtensions: TStringList;
  33. // ------------------------------------------------------------------
  34. implementation
  35. // ------------------------------------------------------------------
  36. // -----------
  37. // ----------- TgxMD5VectorFile -----------
  38. // -----------
  39. function TgxMD5VectorFile.ReadLine: String;
  40. begin
  41. Result := '';
  42. if not Assigned(FMD5String) then
  43. exit;
  44. if FCurrentPos >= FMD5String.Count then
  45. exit;
  46. repeat
  47. Result := FMD5String[FCurrentPos];
  48. Result := StringReplace(Result, '(', '', [rfReplaceAll]);
  49. Result := StringReplace(Result, ')', '', [rfReplaceAll]);
  50. Result := Trim(Result);
  51. Inc(FCurrentPos);
  52. until (Result <> '') or (FCurrentPos >= FMD5String.Count);
  53. end;
  54. class function TgxMD5VectorFile.Capabilities: TDataFileCapabilities;
  55. begin
  56. Result := [dfcRead];
  57. end;
  58. procedure TgxMD5VectorFile.LoadFromStream(aStream: TStream);
  59. procedure AllocateMaterial(var shader: String);
  60. { const
  61. cTexType : array[0..2] of String = ('_local', '_d', '_s');
  62. var
  63. shader_nopath, temp : String;
  64. libmat : TgxLibMaterial;
  65. i, j : Integer;// }
  66. begin
  67. { if Assigned(Owner.MaterialLibrary) then begin
  68. shader:=StringReplace(shader,'/','\',[rfReplaceAll]);
  69. if not DirectoryExists(ExtractFilePath(shader)) then
  70. shader:=ExtractFileName(shader);
  71. if not Assigned(Owner.MaterialLibrary.Materials.GetLibMaterialByName(shader)) then begin
  72. libmat:=Owner.MaterialLibrary.Materials.Add;
  73. libmat.Name:=shader;
  74. for i:=0 to High(cTexType) do begin
  75. temp:=ChangeFileExt(shader, '')+cTexType[i];
  76. for j:=0 to vMD5TextureExtensions.Count-1 do begin
  77. if FileExists(temp+vMD5TextureExtensions[j]) then begin
  78. with libmat.Material.TextureEx.Add do begin
  79. Texture.Image.LoadFromFile(temp+vMD5TextureExtensions[j]);
  80. Texture.Enabled:=True;
  81. end;
  82. Break;
  83. end;
  84. end;
  85. end;
  86. end;
  87. end else// }
  88. shader := '';
  89. end;
  90. function QuaternionMakeFromImag(ix, iy, iz: Single): TQuaternion;
  91. var
  92. rr: Single;
  93. begin
  94. with Result do
  95. begin
  96. ImagPart.X := ix;
  97. ImagPart.Y := iy;
  98. ImagPart.Z := iz;
  99. rr := 1 - (ix * ix) - (iy * iy) - (iz * iz);
  100. if rr < 0 then
  101. RealPart := 0
  102. else
  103. RealPart := sqrt(rr);
  104. end;
  105. end;
  106. procedure ReadBone(BoneNum: Integer; BoneString: String);
  107. var
  108. bonename: String;
  109. pos: TAffineVector;
  110. quat: TQuaternion;
  111. mat, rmat: TMatrix4f;
  112. ParentBoneID: Integer;
  113. bone, parentbone: TgxSkeletonBone;
  114. begin
  115. FTempString.CommaText := BoneString;
  116. bonename := FTempString[0];
  117. ParentBoneID := StrToInt(FTempString[1]);
  118. pos.X := StrToFloatDef(FTempString[2]);
  119. pos.Y := StrToFloatDef(FTempString[4]);
  120. pos.Z := StrToFloatDef(FTempString[3]);
  121. quat := QuaternionMakeFromImag(StrToFloatDef(FTempString[5]),
  122. StrToFloatDef(FTempString[7]),
  123. StrToFloatDef(FTempString[6]));
  124. FFramePositions.Add(pos);
  125. FFrameQuaternions.Add(quat);
  126. if bonename <> '' then
  127. begin
  128. FBoneNames.Add(bonename);
  129. if ParentBoneID = -1 then
  130. bone := TgxSkeletonBone.CreateOwned(Owner.Skeleton.RootBones)
  131. else
  132. begin
  133. parentbone := Owner.Skeleton.RootBones.BoneByID(ParentBoneID);
  134. bone := TgxSkeletonBone.CreateOwned(parentbone);
  135. mat := QuaternionToMatrix(quat);
  136. mat.W := PointMake(pos);
  137. rmat := QuaternionToMatrix(FFrameQuaternions[ParentBoneID]);
  138. rmat.W := PointMake(FFramePositions[ParentBoneID]);
  139. InvertMatrix(rmat);
  140. mat := MatrixMultiply(mat, rmat);
  141. pos := AffineVectorMake(mat.W);
  142. quat := QuaternionFromMatrix(mat);
  143. end;
  144. with bone do
  145. begin
  146. BoneID := BoneNum;
  147. Name := bonename;
  148. end;
  149. end;
  150. FBasePose.Position[BoneNum] := pos;
  151. FBasePose.Quaternion[BoneNum] := quat;
  152. end;
  153. procedure ReadJoints;
  154. var
  155. temp: String;
  156. i: Integer;
  157. begin
  158. i := 0;
  159. repeat
  160. temp := ReadLine;
  161. if temp <> '}' then
  162. begin
  163. ReadBone(i, temp);
  164. Inc(i);
  165. end;
  166. until temp = '}';
  167. Owner.Skeleton.CurrentFrame.Assign(FBasePose);
  168. Owner.Skeleton.CurrentFrame.FlushLocalMatrixList;
  169. Owner.Skeleton.RootBones.PrepareGlobalMatrices;
  170. end;
  171. procedure ReadMesh;
  172. var
  173. temp, shader: String;
  174. mesh: TgxSkeletonMeshObject;
  175. fg: TgxFGVertexIndexList;
  176. vnum, wnum, numverts, numweights: Integer;
  177. VertexWeightID, VertexWeightCount, VertexBoneRef: TgxIntegerList;
  178. VertexWeight: TgxSingleList;
  179. VertexWeighted: TgxAffineVectorList;
  180. blendedVert, transformedVert: TAffineVector;
  181. i, j, k: Integer;
  182. mat: TMatrix4f;
  183. begin
  184. VertexWeightID := TgxIntegerList.Create;
  185. VertexWeightCount := TgxIntegerList.Create;
  186. VertexBoneRef := TgxIntegerList.Create;
  187. VertexWeight := TgxSingleList.Create;
  188. VertexWeighted := TgxAffineVectorList.Create;
  189. numverts := 0;
  190. mesh := TgxSkeletonMeshObject.CreateOwned(Owner.MeshObjects);
  191. fg := TgxFGVertexIndexList.CreateOwned(mesh.FaceGroups);
  192. mesh.Mode := momFaceGroups;
  193. fg.Mode := fgmmTriangles;
  194. repeat
  195. temp := ReadLine;
  196. FTempString.CommaText := temp;
  197. if FTempString.Count > 1 then
  198. begin
  199. temp := LowerCase(FTempString[0]);
  200. if temp = 'shader' then
  201. begin
  202. shader := FTempString[1];
  203. AllocateMaterial(shader);
  204. fg.MaterialName := shader;
  205. end
  206. else if temp = 'numverts' then
  207. begin
  208. numverts := StrToInt(FTempString[1]);
  209. mesh.TexCoords.Count := numverts;
  210. VertexWeightID.Count := numverts;
  211. VertexWeightCount.Count := numverts;
  212. end
  213. else if temp = 'vert' then
  214. begin
  215. if FTempString.Count >= 6 then
  216. begin
  217. vnum := StrToInt(FTempString[1]);
  218. mesh.TexCoords[vnum] :=
  219. AffineVectorMake(StrToFloatDef(FTempString[2]),
  220. 1 - StrToFloatDef(FTempString[3]), 0);
  221. VertexWeightID[vnum] := StrToInt(FTempString[4]);
  222. VertexWeightCount[vnum] := StrToInt(FTempString[5]);
  223. if VertexWeightCount[vnum] > mesh.BonesPerVertex then
  224. mesh.BonesPerVertex := VertexWeightCount[vnum];
  225. end;
  226. end
  227. else if temp = 'numtris' then
  228. begin
  229. fg.VertexIndices.Capacity := StrToInt(FTempString[1]) * 3;
  230. end
  231. else if temp = 'tri' then
  232. begin
  233. if FTempString.Count >= 5 then
  234. begin
  235. fg.VertexIndices.Add(StrToInt(FTempString[2]));
  236. fg.VertexIndices.Add(StrToInt(FTempString[3]));
  237. fg.VertexIndices.Add(StrToInt(FTempString[4]));
  238. end;
  239. end
  240. else if temp = 'numweights' then
  241. begin
  242. numweights := StrToInt(FTempString[1]);
  243. VertexBoneRef.Count := numweights;
  244. VertexWeight.Count := numweights;
  245. VertexWeighted.Count := numweights;
  246. end
  247. else if temp = 'weight' then
  248. begin
  249. if FTempString.Count >= 7 then
  250. begin
  251. wnum := StrToInt(FTempString[1]);
  252. VertexBoneRef[wnum] := StrToInt(FTempString[2]);
  253. VertexWeight[wnum] := StrToFloatDef(FTempString[3]);
  254. VertexWeighted[wnum] :=
  255. AffineVectorMake(StrToFloatDef(FTempString[4]),
  256. StrToFloatDef(FTempString[6]),
  257. StrToFloatDef(FTempString[5]));
  258. end;
  259. end;
  260. end;
  261. until temp = '}';
  262. mesh.Vertices.Count := numverts;
  263. mesh.VerticeBoneWeightCount := numverts;
  264. for i := 0 to numverts - 1 do
  265. begin
  266. blendedVert := NullVector;
  267. for j := 0 to mesh.BonesPerVertex - 1 do
  268. begin
  269. if j < VertexWeightCount[i] then
  270. begin
  271. k := VertexWeightID[i] + j;
  272. mesh.VerticesBonesWeights^[i]^[j].BoneID := VertexBoneRef[k];
  273. mesh.VerticesBonesWeights^[i]^[j].Weight := VertexWeight[k];
  274. mat := Owner.Skeleton.RootBones.BoneByID(VertexBoneRef[k])
  275. .GlobalMatrix;
  276. transformedVert := VectorTransform(VertexWeighted[k], mat);
  277. AddVector(blendedVert, VectorScale(transformedVert, VertexWeight[k]));
  278. end
  279. else
  280. begin
  281. mesh.VerticesBonesWeights^[i]^[j].BoneID := 0;
  282. mesh.VerticesBonesWeights^[i]^[j].Weight := 0;
  283. end;
  284. end;
  285. mesh.Vertices[i] := blendedVert;
  286. end;
  287. mesh.BuildNormals(fg.VertexIndices, momTriangles);
  288. VertexWeightID.Free;
  289. VertexWeightCount.Free;
  290. VertexBoneRef.Free;
  291. VertexWeight.Free;
  292. VertexWeighted.Free;
  293. end;
  294. procedure ReadHierarchy;
  295. var
  296. temp: String;
  297. bone: TgxSkeletonBone;
  298. begin
  299. if not Assigned(FJointFlags) then
  300. begin
  301. FJointFlags := TgxIntegerList.Create;
  302. Assert(Owner.Skeleton.Frames.Count > 0,
  303. 'The md5mesh file must be loaded before md5anim files!');
  304. FJointFlags.Count := Owner.Skeleton.Frames[0].Position.Count;
  305. end;
  306. repeat
  307. temp := ReadLine;
  308. FTempString.CommaText := temp;
  309. if FTempString.Count >= 3 then
  310. begin
  311. bone := Owner.Skeleton.BoneByName(FTempString[0]);
  312. if Assigned(bone) then
  313. FJointFlags[bone.BoneID] := StrToInt(FTempString[2]);
  314. end;
  315. until temp = '}';
  316. end;
  317. procedure ReadBaseFrame;
  318. var
  319. temp: String;
  320. pos: TAffineVector;
  321. quat: TQuaternion;
  322. begin
  323. FFramePositions.Clear;
  324. FFrameQuaternions.Clear;
  325. repeat
  326. temp := ReadLine;
  327. FTempString.CommaText := temp;
  328. if FTempString.Count >= 6 then
  329. begin
  330. pos := AffineVectorMake(StrToFloatDef(FTempString[0]),
  331. StrToFloatDef(FTempString[1]),
  332. StrToFloatDef(FTempString[2]));
  333. quat := QuaternionMakeFromImag(StrToFloatDef(FTempString[3]),
  334. StrToFloatDef(FTempString[4]),
  335. StrToFloatDef(FTempString[5]));
  336. FFramePositions.Add(pos);
  337. FFrameQuaternions.Add(quat);
  338. end;
  339. until temp = '}';
  340. end;
  341. procedure ReadFrame(framenum: Integer);
  342. var
  343. temp: String;
  344. i, j: Integer;
  345. frame: TgxSkeletonFrame;
  346. pos: TAffineVector;
  347. quat: TQuaternion;
  348. begin
  349. frame := Owner.Skeleton.Frames[FFirstFrame + framenum];
  350. frame.TransformMode := sftQuaternion;
  351. frame.Position.Count := FNumJoints;
  352. frame.Quaternion.Count := FNumJoints;
  353. for i := 0 to FJointFlags.Count - 1 do
  354. begin
  355. pos := FFramePositions[i];
  356. quat := FFrameQuaternions[i];
  357. if FJointFlags[i] > 0 then
  358. begin
  359. temp := ReadLine;
  360. FTempString.CommaText := temp;
  361. j := 0;
  362. if FJointFlags[i] and 1 > 0 then
  363. begin
  364. pos.X := StrToFloatDef(FTempString[j]);
  365. Inc(j);
  366. end;
  367. if FJointFlags[i] and 2 > 0 then
  368. begin
  369. pos.Y := StrToFloatDef(FTempString[j]);
  370. Inc(j);
  371. end;
  372. if FJointFlags[i] and 4 > 0 then
  373. begin
  374. pos.Z := StrToFloatDef(FTempString[j]);
  375. Inc(j);
  376. end;
  377. if FJointFlags[i] and 8 > 0 then
  378. begin
  379. quat.ImagPart.X := StrToFloatDef(FTempString[j]);
  380. Inc(j);
  381. end;
  382. if FJointFlags[i] and 16 > 0 then
  383. begin
  384. quat.ImagPart.Y := StrToFloatDef(FTempString[j]);
  385. Inc(j);
  386. end;
  387. if FJointFlags[i] and 32 > 0 then
  388. quat.ImagPart.Z := StrToFloatDef(FTempString[j]);
  389. end;
  390. pos := AffineVectorMake(pos.X, pos.Z, pos.Y);
  391. quat := QuaternionMakeFromImag(quat.ImagPart.X, quat.ImagPart.Z,
  392. quat.ImagPart.Y);
  393. frame.Position[i] := pos;
  394. frame.Quaternion[i] := quat;
  395. end;
  396. end;
  397. procedure InitializeMeshes;
  398. var
  399. i: Integer;
  400. begin
  401. for i := 0 to Owner.MeshObjects.Count - 1 do
  402. TgxSkeletonMeshObject(Owner.MeshObjects[i])
  403. .PrepareBoneMatrixInvertedMeshes;
  404. end;
  405. var
  406. str, temp: String;
  407. nummeshes, md5Version, meshid, i: Integer;
  408. begin
  409. FCurrentPos := 0;
  410. FMD5String := TStringList.Create;
  411. FTempString := TStringList.Create;
  412. FBoneNames := TStringList.Create;
  413. meshid := 0;
  414. nummeshes := 0;
  415. md5Version := 0;
  416. try
  417. FMD5String.LoadFromStream(aStream);
  418. // Version checking
  419. str := ReadLine;
  420. FTempString.CommaText := str;
  421. if FTempString.Count >= 2 then
  422. if LowerCase(FTempString[0]) = 'md5version' then
  423. md5Version := StrToInt(FTempString[1]);
  424. Assert(md5Version = 10, 'Invalid or missing md5Version number.');
  425. repeat
  426. str := ReadLine;
  427. FTempString.CommaText := str;
  428. if FTempString.Count > 1 then
  429. begin
  430. temp := LowerCase(FTempString[0]);
  431. if (temp = 'numjoints') then
  432. begin
  433. FNumJoints := StrToInt(FTempString[1]);
  434. FFramePositions := TgxAffineVectorList.Create;
  435. FFrameQuaternions := TgxQuaternionList.Create;
  436. if Owner.Skeleton.Frames.Count = 0 then
  437. begin
  438. FBasePose := TgxSkeletonFrame.CreateOwned(Owner.Skeleton.Frames);
  439. FBasePose.Position.Count := FNumJoints;
  440. FBasePose.TransformMode := sftQuaternion;
  441. FBasePose.Quaternion.Count := FNumJoints;
  442. end
  443. else
  444. FBasePose := Owner.Skeleton.Frames[0];
  445. end
  446. else if (temp = 'joints') then
  447. begin
  448. ReadJoints;
  449. if Owner is TgxActor then
  450. TgxActor(Owner).Reference := aarSkeleton;
  451. end
  452. else if (temp = 'nummeshes') then
  453. begin
  454. nummeshes := StrToInt(FTempString[1]);
  455. end
  456. else if (temp = 'mesh') then
  457. begin
  458. if meshid < nummeshes then
  459. begin
  460. ReadMesh;
  461. if meshid = nummeshes - 1 then
  462. InitializeMeshes;
  463. Inc(meshid);
  464. end
  465. else
  466. begin
  467. repeat
  468. str := ReadLine;
  469. until str = '}';
  470. end;
  471. end
  472. else if (temp = 'hierarchy') then
  473. begin
  474. ReadHierarchy;
  475. end
  476. else if (temp = 'numframes') then
  477. begin
  478. FNumFrames := StrToInt(FTempString[1]);
  479. if FNumFrames > 0 then
  480. begin
  481. FFirstFrame := Owner.Skeleton.Frames.Count;
  482. for i := 1 to FNumFrames do
  483. TgxSkeletonFrame.CreateOwned(Owner.Skeleton.Frames);
  484. if Owner is TgxActor then
  485. begin
  486. with TgxActor(Owner).Animations.Add do
  487. begin
  488. Name := ChangeFileExt(ExtractFileName(ResourceName), '');
  489. Reference := aarSkeleton;
  490. StartFrame := FFirstFrame;
  491. EndFrame := FFirstFrame + FNumFrames - 1;
  492. end;
  493. end;
  494. end;
  495. end
  496. else if (temp = 'framerate') then
  497. begin
  498. FFrameRate := StrToInt(FTempString[1]);
  499. end
  500. else if (temp = 'baseframe') then
  501. begin
  502. ReadBaseFrame;
  503. end
  504. else if (temp = 'frame') then
  505. begin
  506. ReadFrame(StrToInt(FTempString[1]));
  507. end;
  508. end;
  509. until str = '';
  510. finally
  511. if Assigned(FFramePositions) then
  512. FreeAndNil(FFramePositions);
  513. if Assigned(FFrameQuaternions) then
  514. FreeAndNil(FFrameQuaternions);
  515. if Assigned(FJointFlags) then
  516. FreeAndNil(FJointFlags);
  517. FBoneNames.Free;
  518. FTempString.Free;
  519. FMD5String.Free;
  520. end;
  521. end;
  522. // ------------------------------------------------------------------
  523. // ------------------------------------------------------------------
  524. // ------------------------------------------------------------------
  525. initialization
  526. // ------------------------------------------------------------------
  527. // ------------------------------------------------------------------
  528. // ------------------------------------------------------------------
  529. RegisterVectorFileFormat('md5mesh', 'Doom3 mesh files', TgxMD5VectorFile);
  530. RegisterVectorFileFormat('md5anim', 'Doom3 animation files', TgxMD5VectorFile);
  531. vMD5TextureExtensions := TStringList.Create;
  532. with vMD5TextureExtensions do
  533. begin
  534. Add('.bmp');
  535. Add('.dds');
  536. Add('.jpg');
  537. Add('.tga');
  538. end;
  539. // ------------------------------------------------------------------
  540. // ------------------------------------------------------------------
  541. // ------------------------------------------------------------------
  542. finalization
  543. // ------------------------------------------------------------------
  544. // ------------------------------------------------------------------
  545. // ------------------------------------------------------------------
  546. vMD5TextureExtensions.Free;
  547. end.