GXS.FileGLTF.pas 11 KB

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