GLS.FileMD5.pas 17 KB

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