FileGL2.pas 11 KB

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