GLFileGLTF.pas 11 KB

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