Formats.GL2.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit Formats.GL2;
  5. (*
  6. Ghoul2 (GLM/GLA) file format loading structures
  7. Note: Also referred to as MDX (MDXM/MDXA) format in C source.
  8. *)
  9. interface
  10. uses
  11. System.Classes,
  12. System.SysUtils,
  13. GLS.VectorTypes,
  14. GLS.VectorGeometry;
  15. type
  16. TGLMHeader = record
  17. fileID: array [0 .. 3] of char;
  18. version: integer;
  19. strFile, animName: array [0 .. 63] of char;
  20. animIndex, numBones, numLODs, ofsLODs, numSurfaces, ofsSurfHierarchy, ofsEnd: integer;
  21. end;
  22. TGLMSurfaceHeirachyOffsets = array of integer;
  23. TGLMSurfaceHeirachy = record
  24. name: array [0 .. 63] of char;
  25. flags: LongWord;
  26. shader: array [0 .. 63] of char;
  27. shaderIndex, parentIndex, numChildren: integer;
  28. childIndices: array of integer;
  29. end;
  30. TGLMSurfaceHeader = record
  31. ident, thisSurfaceIndex, ofsHeader, numVerts, ofsVerts, numTriangles, ofsTriangles,
  32. numBoneReferences, ofsBoneReferences, ofsEnd: integer;
  33. end;
  34. TGLMTriangle = record
  35. indices: array [0 .. 2] of integer;
  36. end;
  37. TGLMVertex = record
  38. normal, vertex: TVector3f;
  39. uiNumWeightsAndBoneIndices: Cardinal; // packed int
  40. BoneWeightings: array [0 .. 3] of Byte;
  41. end;
  42. TGLMSurface = record
  43. SurfaceHeader: TGLMSurfaceHeader;
  44. Triangles: array of TGLMTriangle;
  45. Vertices: array of TGLMVertex;
  46. TexCoords: array of TVector2f;
  47. BoneReferences: array of integer;
  48. end;
  49. TGLMLODInfo = record
  50. ofsEnd: integer;
  51. end;
  52. TGLMLODSurfaceOffsets = array of integer;
  53. TGLMLODs = record
  54. LODInfo: TGLMLODInfo;
  55. LODSurfaceOffsets: TGLMLODSurfaceOffsets;
  56. Surfaces: array of TGLMSurface;
  57. end;
  58. TGLAHeader = record
  59. fileID: array [0 .. 3] of char;
  60. version: integer;
  61. name: array [0 .. 63] of char;
  62. fScale: single;
  63. numFrames, ofsFrames, numBones, ofsCompBonePool, ofsSkel, ofsEnd: integer;
  64. end;
  65. TGLABone = array [0 .. 2] of TVector4f;
  66. TGLACompQuatBone = array [0 .. 6] of Word; { 14 bytes }
  67. TGLASkeletonOffsets = array of integer;
  68. TGLASkeleton = record
  69. name: array [0 .. 63] of char;
  70. flags: LongWord;
  71. parent: integer;
  72. BasePoseMat, BasePoseMatInv: TGLABone;
  73. numChildren: integer;
  74. children: array of integer;
  75. end;
  76. // Ghoul2 Model structure
  77. TFileGLM = class
  78. public
  79. ModelHeader: TGLMHeader;
  80. SurfaceHeirachyOffsets: TGLMSurfaceHeirachyOffsets;
  81. SurfaceHeirachy: array of TGLMSurfaceHeirachy;
  82. LODs: array of TGLMLODs;
  83. procedure LoadFromStream(aStream: TStream);
  84. end;
  85. // Ghoul2 Animation structure
  86. TFileGLA = class
  87. public
  88. AnimHeader: TGLAHeader;
  89. SkeletonOffsets: TGLASkeletonOffsets;
  90. Skeleton: array of TGLASkeleton;
  91. BoneIndices: array of integer;
  92. CompBonePool: array of TGLACompQuatBone;
  93. function GetCompressedMatrix(Frame, Bone: integer): TGLACompQuatBone;
  94. function GetUnCompressedMatrix(Frame, Bone: integer): TGLMatrix;
  95. procedure LoadFromStream(aStream: TStream);
  96. end;
  97. function G2_GetVertWeights(const vert: TGLMVertex): integer;
  98. function G2_GetVertBoneIndex(const vert: TGLMVertex; iWeightNum: integer): integer;
  99. function G2_GetVertBoneWeight(const vert: TGLMVertex; iWeightNum: Cardinal;
  100. var fTotalWeight: single; const iNumWeights: Cardinal): single;
  101. procedure MC_UnCompressQuat(var mat: TGLMatrix; const comp: TGLACompQuatBone);
  102. // ------------------------------------------------------------------
  103. implementation
  104. // ------------------------------------------------------------------
  105. // ------------------
  106. // ------------------ Misc routines ------------------
  107. // ------------------
  108. // Adapted from mdx_format.h
  109. // static inline int G2_GetVertWeights( const mdxmVertex_t *pVert )
  110. // static inline int G2_GetVertBoneIndex( const mdxmVertex_t *pVert, const int iWeightNum)
  111. // static inline float G2_GetVertBoneWeight( const mdxmVertex_t *pVert, const int iWeightNum, float &fTotalWeight, int iNumWeights )
  112. function G2_GetVertWeights(const vert: TGLMVertex): integer;
  113. begin
  114. // Get number of bones per vertex (0..3)+1 = (1..4)
  115. result := (vert.uiNumWeightsAndBoneIndices shr 30) + 1;
  116. end;
  117. function G2_GetVertBoneIndex(const vert: TGLMVertex; iWeightNum: integer): integer;
  118. begin
  119. // Extract the bone reference array index, a 5-bit integer
  120. result := (vert.uiNumWeightsAndBoneIndices shr (5 * iWeightNum)) and 31;
  121. end;
  122. function G2_GetVertBoneWeight(const vert: TGLMVertex; iWeightNum: Cardinal;
  123. var fTotalWeight: single; const iNumWeights: Cardinal): single;
  124. var
  125. fBoneWeight: single;
  126. iTemp: Cardinal;
  127. begin
  128. if (iWeightNum = iNumWeights - 1) then
  129. begin
  130. // No need to calculate final weight value, return the
  131. // weight left over out of 1
  132. fBoneWeight := 1 - fTotalWeight;
  133. end
  134. else
  135. begin
  136. // Get the initial 8-bit bone weight
  137. iTemp := vert.BoneWeightings[iWeightNum];
  138. // Get the 2-bit overflow and 'or' it to the front of the
  139. // weight to get 10-bit integer weight (0..1023)
  140. iTemp := iTemp or ((vert.uiNumWeightsAndBoneIndices shr (12 + (iWeightNum * 2))) and $300);
  141. // Convert to floating point weight (0..1)
  142. fBoneWeight := iTemp / 1023;
  143. // Accumulate total weight
  144. fTotalWeight := fTotalWeight + fBoneWeight;
  145. end;
  146. result := fBoneWeight;
  147. end;
  148. // Adapted from matcomp.c
  149. // void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp)
  150. procedure MC_UnCompressQuat(var mat: TGLMatrix; const comp: TGLACompQuatBone);
  151. begin
  152. mat := QuaternionToMatrix(QuaternionMake([comp[1] - 32726, comp[2] - 32726, comp[3] - 32726],
  153. comp[0] - 32726));
  154. mat.V[3] := VectorMake(comp[4] / 64 - 512, comp[5] / 64 - 512, comp[6] / 64 - 512, 1);
  155. end;
  156. // ------------------
  157. // ------------------ TFileGLM ------------------
  158. // ------------------
  159. procedure TFileGLM.LoadFromStream(aStream: TStream);
  160. var
  161. idstr: array [0 .. 3] of char;
  162. i, j: integer;
  163. ofs, LODofs: int64;
  164. begin
  165. aStream.Read(idstr, sizeof(idstr));
  166. aStream.Position := 0;
  167. if not(idstr = '2LGM') then
  168. begin
  169. raise Exception.Create(Format('Unknown or incorrect identity tag: [%s]', [idstr]));
  170. exit;
  171. end;
  172. aStream.Read(ModelHeader, sizeof(ModelHeader));
  173. if ModelHeader.version <> 6 then
  174. raise Exception.Create(Format('Only GLM (MDXM) version 6 is supported. File is version %d.',
  175. [ModelHeader.version]));
  176. SetLength(SurfaceHeirachyOffsets, ModelHeader.numSurfaces);
  177. aStream.Read(SurfaceHeirachyOffsets[0], sizeof(integer) * ModelHeader.numSurfaces);
  178. SetLength(SurfaceHeirachy, ModelHeader.numSurfaces);
  179. for i := 0 to ModelHeader.numSurfaces - 1 do
  180. with SurfaceHeirachy[i] do
  181. begin
  182. aStream.Read(name, Length(name));
  183. aStream.Read(flags, sizeof(LongWord));
  184. aStream.Read(shader, Length(shader));
  185. aStream.Read(shaderIndex, sizeof(integer));
  186. aStream.Read(parentIndex, sizeof(integer));
  187. aStream.Read(numChildren, sizeof(integer));
  188. if numChildren > 0 then
  189. begin
  190. SetLength(childIndices, numChildren);
  191. aStream.Read(childIndices[0], numChildren * sizeof(integer));
  192. end
  193. else
  194. SetLength(childIndices, 0);
  195. end;
  196. SetLength(LODs, ModelHeader.numLODs);
  197. for i := 0 to ModelHeader.numLODs - 1 do
  198. with LODs[i] do
  199. begin
  200. LODofs := aStream.Position;
  201. aStream.Read(LODInfo, sizeof(LODInfo));
  202. SetLength(LODSurfaceOffsets, ModelHeader.numSurfaces);
  203. aStream.Read(LODSurfaceOffsets[0], sizeof(integer) * ModelHeader.numSurfaces);
  204. SetLength(Surfaces, ModelHeader.numSurfaces);
  205. for j := 0 to ModelHeader.numSurfaces - 1 do
  206. with Surfaces[j] do
  207. begin
  208. ofs := aStream.Position;
  209. aStream.Read(SurfaceHeader, sizeof(TGLMSurfaceHeader));
  210. SetLength(Triangles, SurfaceHeader.numTriangles);
  211. SetLength(Vertices, SurfaceHeader.numVerts);
  212. SetLength(TexCoords, SurfaceHeader.numVerts);
  213. SetLength(BoneReferences, SurfaceHeader.numBoneReferences);
  214. aStream.Position := ofs + SurfaceHeader.ofsTriangles;
  215. aStream.Read(Triangles[0], SurfaceHeader.numTriangles * sizeof(TGLMTriangle));
  216. aStream.Position := ofs + SurfaceHeader.ofsVerts;
  217. aStream.Read(Vertices[0], SurfaceHeader.numVerts * sizeof(TGLMVertex));
  218. aStream.Read(TexCoords[0], SurfaceHeader.numVerts * sizeof(TVector2f));
  219. aStream.Position := ofs + SurfaceHeader.ofsBoneReferences;
  220. aStream.Read(BoneReferences[0], SurfaceHeader.numBoneReferences * sizeof(integer));
  221. aStream.Position := ofs + SurfaceHeader.ofsEnd;
  222. end;
  223. aStream.Position := LODofs + LODInfo.ofsEnd;
  224. end;
  225. end;
  226. // ------------------
  227. // ------------------ TFileGLA ------------------
  228. // ------------------
  229. function TFileGLA.GetCompressedMatrix(Frame, Bone: integer): TGLACompQuatBone;
  230. begin
  231. result := CompBonePool[BoneIndices[Frame * AnimHeader.numBones + Bone]];
  232. end;
  233. // GetUnCompressedMatrix
  234. //
  235. function TFileGLA.GetUnCompressedMatrix(Frame, Bone: integer): TGLMatrix;
  236. begin
  237. MC_UnCompressQuat(result, CompBonePool[BoneIndices[Frame * AnimHeader.numBones + Bone]]);
  238. end;
  239. procedure TFileGLA.LoadFromStream(aStream: TStream);
  240. var
  241. idstr: array [0 .. 3] of char;
  242. i, temp: integer;
  243. buf: array of array [0 .. 2] of Byte;
  244. begin
  245. aStream.Read(idstr, sizeof(idstr));
  246. aStream.Position := 0;
  247. if not(idstr = '2LGA') then
  248. begin
  249. raise Exception.Create(Format('Unknown or incorrect identity tag: [%s]', [idstr]));
  250. exit;
  251. end;
  252. aStream.Read(AnimHeader, sizeof(AnimHeader));
  253. if AnimHeader.version <> 6 then
  254. raise Exception.Create(Format('Only GLA (MDXA) version 6 is supported. File is version %d.',
  255. [AnimHeader.version]));
  256. SetLength(SkeletonOffsets, AnimHeader.numBones);
  257. aStream.Read(SkeletonOffsets[0], sizeof(integer) * AnimHeader.numBones);
  258. SetLength(Skeleton, AnimHeader.numBones);
  259. for i := 0 to AnimHeader.numBones - 1 do
  260. with Skeleton[i] do
  261. begin
  262. aStream.Read(name, Length(name));
  263. aStream.Read(flags, sizeof(LongWord));
  264. aStream.Read(parent, sizeof(integer));
  265. aStream.Read(BasePoseMat, sizeof(TGLABone));
  266. aStream.Read(BasePoseMatInv, sizeof(TGLABone));
  267. aStream.Read(numChildren, sizeof(integer));
  268. if numChildren > 0 then
  269. begin
  270. SetLength(children, numChildren);
  271. aStream.Read(children[0], numChildren * sizeof(integer));
  272. end
  273. else
  274. SetLength(children, 0);
  275. end;
  276. aStream.Position := AnimHeader.ofsFrames;
  277. SetLength(BoneIndices, AnimHeader.numFrames * AnimHeader.numBones);
  278. SetLength(buf, AnimHeader.numFrames * AnimHeader.numBones * 3);
  279. aStream.Read(buf[0], AnimHeader.numFrames * AnimHeader.numBones * 3);
  280. for i := 0 to AnimHeader.numFrames * AnimHeader.numBones - 1 do
  281. BoneIndices[i] := (buf[i][2] shl 16) or (buf[i][1] shl 8) or buf[i][0];
  282. SetLength(buf, 0);
  283. aStream.Position := AnimHeader.ofsCompBonePool;
  284. temp := AnimHeader.ofsEnd - AnimHeader.ofsCompBonePool;
  285. SetLength(CompBonePool, temp div sizeof(TGLACompQuatBone));
  286. aStream.Read(CompBonePool[0], temp);
  287. end;
  288. end.