GLFileGLB.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLFileGLB;
  5. (* GLB binary file for glTF format implementation. *)
  6. interface
  7. uses
  8. System.Classes,
  9. System.SysUtils,
  10. GLVectorFileObjects,
  11. GLTexture,
  12. GLApplicationFileIO,
  13. GLVectorTypes,
  14. GLVectorLists,
  15. GLVectorGeometry,
  16. GLMaterial,
  17. GLS.Utils,
  18. GLPersistentClasses;
  19. type
  20. (* The GLB binary glTF format is a runtime asset delivery format
  21. for GL APIs: WebGL, OpenGL ES OpenGL and Vulkan. *)
  22. TGLBVectorFile = class(TGLVectorFile)
  23. public
  24. class function Capabilities: TGLDataFileCapabilities; override;
  25. procedure LoadFromStream(aStream: TStream); override;
  26. procedure SaveToStream(aStream: TStream); override;
  27. end;
  28. // ------------------------------------------------------------------
  29. implementation
  30. // ------------------------------------------------------------------
  31. // ------------------
  32. // ------------------ TGLGLBVectorFile ------------------
  33. // ------------------
  34. class function TGLBVectorFile.Capabilities: TGLDataFileCapabilities;
  35. begin
  36. Result := [dfcRead, dfcWrite];
  37. end;
  38. procedure TGLBVectorFile.LoadFromStream(aStream: TStream);
  39. procedure AllocateMaterial(const name: String);
  40. var
  41. matLib: TGLMaterialLibrary;
  42. begin
  43. if Owner is TGLBaseMesh then
  44. begin
  45. matLib := TGLBaseMesh(GetOwner).MaterialLibrary;
  46. if Assigned(matLib) then
  47. begin
  48. if matLib.Materials.GetLibMaterialByName(name) = nil then
  49. begin
  50. if CompareText(name, 'null.bmp') <> 0 then
  51. begin
  52. try
  53. matLib.AddTextureMaterial(name, name)
  54. except
  55. on E: ETexture do
  56. begin
  57. if not Owner.IgnoreMissingTextures then
  58. raise;
  59. end;
  60. end;
  61. end
  62. else
  63. matLib.AddTextureMaterial(name, '');
  64. end;
  65. end;
  66. end;
  67. end;
  68. var
  69. i, j, k, nVert, nTex, firstFrame: Integer;
  70. nbBones, boneID: Integer;
  71. mesh: TGLSkeletonMeshObject;
  72. sl, tl: TStringList;
  73. bone: TGLSkeletonBone;
  74. frame: TGLSkeletonFrame;
  75. faceGroup: TFGVertexNormalTexIndexList;
  76. v: TAffineVector;
  77. boneIDs: TVertexBoneWeightDynArray;
  78. weightCount: Integer;
  79. begin
  80. sl := TStringList.Create;
  81. tl := TStringList.Create;
  82. try
  83. sl.LoadFromStream(aStream);
  84. if sl[0] <> 'version 1' then
  85. raise Exception.Create('SMD version 1 required');
  86. if sl[1] <> 'nodes' then
  87. raise Exception.Create('nodes not found');
  88. if sl.IndexOf('triangles') >= 0 then
  89. begin
  90. mesh := TGLSkeletonMeshObject.CreateOwned(Owner.MeshObjects);
  91. mesh.Mode := momFaceGroups;
  92. end
  93. else if Owner.MeshObjects.Count > 0 then
  94. mesh := (Owner.MeshObjects[0] as TGLSkeletonMeshObject)
  95. else
  96. raise Exception.Create('SMD is an animation, load model SMD first.');
  97. // read skeleton nodes
  98. i := 2;
  99. if Owner.Skeleton.RootBones.Count = 0 then
  100. begin
  101. // new bone structure
  102. while sl[i] <> 'end' do
  103. begin
  104. tl.CommaText := sl[i];
  105. with Owner.Skeleton do
  106. if (tl[2] <> '-1') then
  107. bone := TGLSkeletonBone.CreateOwned
  108. (RootBones.BoneByID(StrToInt(tl[2])))
  109. else
  110. bone := TGLSkeletonBone.CreateOwned(RootBones);
  111. if Assigned(bone) then
  112. begin
  113. bone.boneID := StrToInt(tl[0]);
  114. bone.name := tl[1];
  115. end;
  116. Inc(i);
  117. end;
  118. end
  119. else
  120. begin
  121. // animation file, skip structure
  122. while sl[i] <> 'end' do
  123. Inc(i);
  124. end;
  125. Inc(i);
  126. if sl[i] <> 'skeleton' then
  127. raise Exception.Create('skeleton not found');
  128. Inc(i);
  129. // read animation time frames
  130. nbBones := Owner.Skeleton.RootBones.BoneCount - 1;
  131. firstFrame := Owner.Skeleton.Frames.Count;
  132. while sl[i] <> 'end' do
  133. begin
  134. if Copy(sl[i], 1, 5) <> 'time ' then
  135. raise Exception.Create('time not found, got: ' + sl[i]);
  136. frame := TGLSkeletonFrame.CreateOwned(Owner.Skeleton.Frames);
  137. frame.name := ResourceName + ' ' + sl[i];
  138. Inc(i);
  139. while Pos(Copy(sl[i], 1, 1), ' 1234567890') > 0 do
  140. begin
  141. tl.CommaText := sl[i];
  142. while StrToInt(tl[0]) > frame.Position.Count do
  143. begin
  144. frame.Position.Add(NullVector);
  145. frame.Rotation.Add(NullVector);
  146. end;
  147. frame.Position.Add(GLS.Utils.StrToFloatDef(tl[1]),
  148. GLS.Utils.StrToFloatDef(tl[2]), GLS.Utils.StrToFloatDef(tl[3]));
  149. v := AffineVectorMake(GLS.Utils.StrToFloatDef(tl[4]),
  150. GLS.Utils.StrToFloatDef(tl[5]), GLS.Utils.StrToFloatDef(tl[6]));
  151. frame.Rotation.Add(v);
  152. Inc(i);
  153. end;
  154. while frame.Position.Count < nbBones do
  155. begin
  156. frame.Position.Add(NullVector);
  157. frame.Rotation.Add(NullVector);
  158. end;
  159. Assert(frame.Position.Count = nbBones, 'Invalid number of bones in frame '
  160. + IntToStr(Owner.Skeleton.Frames.Count));
  161. end;
  162. if Owner is TGLActor then
  163. with TGLActor(Owner).Animations.Add do
  164. begin
  165. k := Pos('.', ResourceName);
  166. if k > 0 then
  167. Name := Copy(ResourceName, 1, k - 1)
  168. else
  169. Name := ResourceName;
  170. Reference := aarSkeleton;
  171. StartFrame := firstFrame;
  172. EndFrame := Self.Owner.Skeleton.Frames.Count - 1;
  173. end;
  174. Inc(i);
  175. if (i < sl.Count) and (sl[i] = 'triangles') then
  176. begin
  177. // read optional mesh data
  178. Inc(i);
  179. if mesh.BonesPerVertex < 1 then
  180. mesh.BonesPerVertex := 1;
  181. faceGroup := nil;
  182. while sl[i] <> 'end' do
  183. begin
  184. if (faceGroup = nil) or (faceGroup.MaterialName <> sl[i]) then
  185. begin
  186. faceGroup := TFGVertexNormalTexIndexList.CreateOwned(mesh.FaceGroups);
  187. faceGroup.Mode := fgmmTriangles;
  188. faceGroup.MaterialName := sl[i];
  189. AllocateMaterial(sl[i]);
  190. end;
  191. Inc(i);
  192. for k := 1 to 3 do
  193. with mesh do
  194. begin
  195. tl.CommaText := sl[i];
  196. if tl.Count > 9 then
  197. begin
  198. // specifies bones and weights
  199. weightCount := StrToInt(tl[9]);
  200. SetLength(boneIDs, weightCount);
  201. for j := 0 to weightCount - 1 do
  202. begin
  203. boneIDs[j].boneID := StrToInt(tl[10 + j * 2]);
  204. boneIDs[j].Weight := GLS.Utils.StrToFloatDef(tl[11 + j * 2]);
  205. end;
  206. nVert := FindOrAdd(boneIDs,
  207. AffineVectorMake(GLS.Utils.StrToFloatDef(tl[1]),
  208. GLS.Utils.StrToFloatDef(tl[2]), GLS.Utils.StrToFloatDef(tl[3])),
  209. AffineVectorMake(GLS.Utils.StrToFloatDef(tl[4]),
  210. GLS.Utils.StrToFloatDef(tl[5]),
  211. GLS.Utils.StrToFloatDef(tl[6])));
  212. nTex := TexCoords.FindOrAdd
  213. (AffineVectorMake(GLS.Utils.StrToFloatDef(tl[7]),
  214. GLS.Utils.StrToFloatDef(tl[8]), 0));
  215. faceGroup.Add(nVert, nVert, nTex);
  216. Inc(i);
  217. end
  218. else
  219. begin
  220. // simple format
  221. boneID := StrToInt(tl[0]);
  222. nVert := FindOrAdd(boneID,
  223. AffineVectorMake(GLS.Utils.StrToFloatDef(tl[1]),
  224. GLS.Utils.StrToFloatDef(tl[2]), GLS.Utils.StrToFloatDef(tl[3])),
  225. AffineVectorMake(GLS.Utils.StrToFloatDef(tl[4]),
  226. GLS.Utils.StrToFloatDef(tl[5]), GLS.Utils.StrToFloatDef(tl[6])));
  227. nTex := TexCoords.FindOrAdd
  228. (AffineVectorMake(GLS.Utils.StrToFloatDef(tl[7]),
  229. GLS.Utils.StrToFloatDef(tl[8]), 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 TGLBVectorFile.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: TGLSkeletonBone; 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 TGLSkeletonMeshObject then
  293. with TGLSkeletonMeshObject(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. RegisterVectorFileFormat('glb', 'Binary glTF files', TGLBVectorFile);
  328. end.