2
0

GLFileGLB.pas 11 KB

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