2
0

GLS.FileGLTF.pas 11 KB

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