2
0

GLS.FileSMD.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.FileSMD;
  5. (* SMD vector file format implementation *)
  6. interface
  7. uses
  8. System.Classes,
  9. System.SysUtils,
  10. GLS.VectorFileObjects,
  11. GLS.VectorLists,
  12. GLS.Texture,
  13. GLS.PersistentClasses,
  14. GLS.ApplicationFileIO,
  15. Stage.VectorTypes,
  16. Stage.VectorGeometry,
  17. GLS.Material,
  18. Stage.Strings,
  19. Stage.Utils;
  20. type
  21. (* The SMD vector file is Half-life's skeleton format.
  22. The SMD is a text-based file format. They come in two flavors: one that
  23. old Skeleton and triangle (mesh) data, and animation files that store
  24. Skeleton frames.
  25. This reader curently reads both, but requires that the main file
  26. (the one with mesh data) be read first. *)
  27. TGLSMDVectorFile = class(TGLVectorFile)
  28. public
  29. class function Capabilities: TGLDataFileCapabilities; override;
  30. procedure LoadFromStream(aStream: TStream); override;
  31. procedure SaveToStream(aStream: TStream); override;
  32. end;
  33. // ------------------------------------------------------------------
  34. implementation
  35. // ------------------------------------------------------------------
  36. // ------------------
  37. // ------------------ TGLSMDVectorFile ------------------
  38. // ------------------
  39. class function TGLSMDVectorFile.Capabilities: TGLDataFileCapabilities;
  40. begin
  41. Result := [dfcRead, dfcWrite];
  42. end;
  43. procedure TGLSMDVectorFile.LoadFromStream(aStream: TStream);
  44. procedure AllocateMaterial(const name: String);
  45. var
  46. matLib: TGLMaterialLibrary;
  47. begin
  48. if Owner is TGLBaseMesh then
  49. begin
  50. matLib := TGLBaseMesh(GetOwner).MaterialLibrary;
  51. if Assigned(matLib) then
  52. begin
  53. if matLib.Materials.GetLibMaterialByName(name) = nil then
  54. begin
  55. if CompareText(name, 'null.bmp') <> 0 then
  56. begin
  57. try
  58. matLib.AddTextureMaterial(name, name)
  59. except
  60. on E: ETexture do
  61. begin
  62. if not Owner.IgnoreMissingTextures then
  63. raise;
  64. end;
  65. end;
  66. end
  67. else
  68. matLib.AddTextureMaterial(name, '');
  69. end;
  70. end;
  71. end;
  72. end;
  73. var
  74. i, j, k, nVert, nTex, firstFrame: Integer;
  75. nbBones, boneID: Integer;
  76. mesh: TGLSkeletonMeshObject;
  77. sl, tl: TStringList;
  78. bone: TGLSkeletonBone;
  79. frame: TGLSkeletonFrame;
  80. faceGroup: TFGVertexNormalTexIndexList;
  81. v: TAffineVector;
  82. boneIDs: TGLVertexBoneWeightDynArray;
  83. weightCount: Integer;
  84. begin
  85. sl := TStringList.Create;
  86. tl := TStringList.Create;
  87. try
  88. sl.LoadFromStream(aStream);
  89. if sl[0] <> 'version 1' then
  90. raise Exception.Create('SMD version 1 required');
  91. if sl[1] <> 'nodes' then
  92. raise Exception.Create('nodes not found');
  93. if sl.IndexOf('triangles') >= 0 then
  94. begin
  95. mesh := TGLSkeletonMeshObject.CreateOwned(Owner.MeshObjects);
  96. mesh.Mode := momFaceGroups;
  97. end
  98. else if Owner.MeshObjects.Count > 0 then
  99. mesh := (Owner.MeshObjects[0] as TGLSkeletonMeshObject)
  100. else
  101. raise Exception.Create('SMD is an animation, load model SMD first.');
  102. // read skeleton nodes
  103. i := 2;
  104. if Owner.Skeleton.RootBones.Count = 0 then
  105. begin
  106. // new bone structure
  107. while sl[i] <> 'end' do
  108. begin
  109. tl.CommaText := sl[i];
  110. with Owner.Skeleton do
  111. if (tl[2] <> '-1') then
  112. bone := TGLSkeletonBone.CreateOwned
  113. (RootBones.BoneByID(StrToInt(tl[2])))
  114. else
  115. bone := TGLSkeletonBone.CreateOwned(RootBones);
  116. if Assigned(bone) then
  117. begin
  118. bone.boneID := StrToInt(tl[0]);
  119. bone.name := tl[1];
  120. end;
  121. Inc(i);
  122. end;
  123. end
  124. else
  125. begin
  126. // animation file, skip structure
  127. while sl[i] <> 'end' do
  128. Inc(i);
  129. end;
  130. Inc(i);
  131. if sl[i] <> 'skeleton' then
  132. raise Exception.Create('skeleton not found');
  133. Inc(i);
  134. // read animation time frames
  135. nbBones := Owner.Skeleton.RootBones.BoneCount - 1;
  136. firstFrame := Owner.Skeleton.Frames.Count;
  137. while sl[i] <> 'end' do
  138. begin
  139. if Copy(sl[i], 1, 5) <> 'time ' then
  140. raise Exception.Create('time not found, got: ' + sl[i]);
  141. frame := TGLSkeletonFrame.CreateOwned(Owner.Skeleton.Frames);
  142. frame.name := ResourceName + ' ' + sl[i];
  143. Inc(i);
  144. while Pos(Copy(sl[i], 1, 1), ' 1234567890') > 0 do
  145. begin
  146. tl.CommaText := sl[i];
  147. while StrToInt(tl[0]) > frame.Position.Count do
  148. begin
  149. frame.Position.Add(NullVector);
  150. frame.Rotation.Add(NullVector);
  151. end;
  152. frame.Position.Add(GLStrToFloatDef(tl[1],0),
  153. GLStrToFloatDef(tl[2],0), GLStrToFloatDef(tl[3],0));
  154. v := AffineVectorMake(GLStrToFloatDef(tl[4],0),
  155. GLStrToFloatDef(tl[5],0), GLStrToFloatDef(tl[6],0));
  156. frame.Rotation.Add(v);
  157. Inc(i);
  158. end;
  159. while frame.Position.Count < nbBones do
  160. begin
  161. frame.Position.Add(NullVector);
  162. frame.Rotation.Add(NullVector);
  163. end;
  164. Assert(frame.Position.Count = nbBones, 'Invalid number of bones in frame '
  165. + IntToStr(Owner.Skeleton.Frames.Count));
  166. end;
  167. if Owner is TGLActor then
  168. with TGLActor(Owner).Animations.Add do
  169. begin
  170. k := Pos('.', ResourceName);
  171. if k > 0 then
  172. Name := Copy(ResourceName, 1, k - 1)
  173. else
  174. Name := ResourceName;
  175. Reference := aarSkeleton;
  176. StartFrame := firstFrame;
  177. EndFrame := Self.Owner.Skeleton.Frames.Count - 1;
  178. end;
  179. Inc(i);
  180. if (i < sl.Count) and (sl[i] = 'triangles') then
  181. begin
  182. // read optional mesh data
  183. Inc(i);
  184. if mesh.BonesPerVertex < 1 then
  185. mesh.BonesPerVertex := 1;
  186. faceGroup := nil;
  187. while sl[i] <> 'end' do
  188. begin
  189. if (faceGroup = nil) or (faceGroup.MaterialName <> sl[i]) then
  190. begin
  191. faceGroup := TFGVertexNormalTexIndexList.CreateOwned(mesh.FaceGroups);
  192. faceGroup.Mode := fgmmTriangles;
  193. faceGroup.MaterialName := sl[i];
  194. AllocateMaterial(sl[i]);
  195. end;
  196. Inc(i);
  197. for k := 1 to 3 do
  198. with mesh do
  199. begin
  200. tl.CommaText := sl[i];
  201. if tl.Count > 9 then
  202. begin
  203. // Half-Life 2 SMD, specifies bones and weights
  204. weightCount := StrToInt(tl[9]);
  205. SetLength(boneIDs, weightCount);
  206. for j := 0 to weightCount - 1 do
  207. begin
  208. boneIDs[j].boneID := StrToInt(tl[10 + j * 2]);
  209. boneIDs[j].Weight := GLStrToFloatDef(tl[11 + j * 2],0);
  210. end;
  211. nVert := FindOrAdd(boneIDs,
  212. AffineVectorMake(GLStrToFloatDef(tl[1],0),
  213. GLStrToFloatDef(tl[2],0), GLStrToFloatDef(tl[3],0)),
  214. AffineVectorMake(GLStrToFloatDef(tl[4],0),
  215. GLStrToFloatDef(tl[5],0), GLStrToFloatDef(tl[6],0)));
  216. nTex := TexCoords.FindOrAdd
  217. (AffineVectorMake(GLStrToFloatDef(tl[7],0),
  218. GLStrToFloatDef(tl[8],0), 0));
  219. faceGroup.Add(nVert, nVert, nTex);
  220. Inc(i);
  221. end
  222. else
  223. begin
  224. // Half-Life 1 simple format
  225. boneID := StrToInt(tl[0]);
  226. nVert := FindOrAdd(boneID,
  227. AffineVectorMake(GLStrToFloatDef(tl[1],0),
  228. GLStrToFloatDef(tl[2],0), GLStrToFloatDef(tl[3],0)),
  229. AffineVectorMake(GLStrToFloatDef(tl[4],0),
  230. GLStrToFloatDef(tl[5],0), GLStrToFloatDef(tl[6],0)));
  231. nTex := TexCoords.FindOrAdd
  232. (AffineVectorMake(GLStrToFloatDef(tl[7],0),
  233. GLStrToFloatDef(tl[8],0), 0));
  234. faceGroup.Add(nVert, nVert, nTex);
  235. Inc(i);
  236. end;
  237. end;
  238. end;
  239. Owner.Skeleton.RootBones.PrepareGlobalMatrices;
  240. mesh.PrepareBoneMatrixInvertedMeshes;
  241. end;
  242. finally
  243. tl.Free;
  244. sl.Free;
  245. end;
  246. end;
  247. procedure TGLSMDVectorFile.SaveToStream(aStream: TStream);
  248. var
  249. str, nodes: TStrings;
  250. i, j, k, l, b: Integer;
  251. p, r, v, n, t: TAffineVector;
  252. procedure GetNodesFromBonesRecurs(bone: TGLSkeletonBone; ParentID: Integer;
  253. bl: TStrings);
  254. var
  255. i: Integer;
  256. begin
  257. bl.Add(Format('%3d "%s" %3d', [bone.boneID, bone.name, ParentID]));
  258. for i := 0 to bone.Count - 1 do
  259. GetNodesFromBonesRecurs(bone.Items[i], bone.boneID, bl);
  260. end;
  261. begin
  262. str := TStringList.Create;
  263. nodes := TStringList.Create;
  264. try
  265. str.Add('version 1');
  266. // Add the bones
  267. str.Add('nodes');
  268. for i := 0 to Owner.Skeleton.RootBones.Count - 1 do
  269. begin
  270. GetNodesFromBonesRecurs(Owner.Skeleton.RootBones[i], -1, nodes);
  271. end;
  272. str.AddStrings(nodes);
  273. str.Add('end');
  274. // Now add the relavent frames
  275. if Owner.Skeleton.Frames.Count > 0 then
  276. begin
  277. str.Add('skeleton');
  278. for i := 0 to Owner.Skeleton.Frames.Count - 1 do
  279. begin
  280. str.Add(Format('time %d', [i]));
  281. for j := 0 to Owner.Skeleton.Frames[i].Position.Count - 1 do
  282. begin
  283. p := Owner.Skeleton.Frames[i].Position[j];
  284. r := Owner.Skeleton.Frames[i].Rotation[j];
  285. str.Add(StringReplace(Format('%3d %.6f %.6f %.6f %.6f %.6f %.6f',
  286. [j, p.X, p.Y, p.Z, r.X, r.Y, r.Z]), ',', '.', [rfReplaceAll]));
  287. end;
  288. end;
  289. str.Add('end');
  290. end;
  291. // Add the mesh data
  292. if Owner.MeshObjects.Count > 0 then
  293. begin
  294. str.Add('triangles');
  295. for i := 0 to Owner.MeshObjects.Count - 1 do
  296. if Owner.MeshObjects[i] is TGLSkeletonMeshObject then
  297. with TGLSkeletonMeshObject(Owner.MeshObjects[i]) do
  298. begin
  299. for j := 0 to FaceGroups.Count - 1 do
  300. with TFGVertexNormalTexIndexList(FaceGroups[j]) do
  301. begin
  302. for k := 0 to (VertexIndices.Count div 3) - 1 do
  303. begin
  304. str.Add(MaterialName);
  305. for l := 0 to 2 do
  306. begin
  307. v := Vertices[VertexIndices[3 * k + l]];
  308. n := Normals[NormalIndices[3 * k + l]];
  309. t := TexCoords[TexCoordIndices[3 * k + l]];
  310. b := VerticesBonesWeights^[VertexIndices[3 * k + l]]^
  311. [0].boneID;
  312. str.Add(StringReplace
  313. (Format('%3d %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f',
  314. [b, v.X, v.Y, v.Z, n.X, n.Y, n.Z, t.X, t.Y]), ',', '.',
  315. [rfReplaceAll]));
  316. end;
  317. end;
  318. end;
  319. end;
  320. str.Add('end');
  321. end;
  322. str.SaveToStream(aStream);
  323. finally
  324. str.Free;
  325. nodes.Free;
  326. end;
  327. end;
  328. // ------------------------------------------------------------------
  329. initialization
  330. // ------------------------------------------------------------------
  331. RegisterVectorFileFormat('smd', 'Half-Life SMD files', TGLSMDVectorFile);
  332. end.