Formats.GL2.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //
  2. // The graphics engine 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. Stage.VectorGeometry,
  14. Stage.VectorTypes;
  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. implementation // -----------------------------------------------------------
  103. // ------------------
  104. // ------------------ Misc routines ------------------
  105. // ------------------
  106. // Adapted from mdx_format.h
  107. // static inline int G2_GetVertWeights( const mdxmVertex_t *pVert )
  108. // static inline int G2_GetVertBoneIndex( const mdxmVertex_t *pVert, const int iWeightNum)
  109. // static inline float G2_GetVertBoneWeight( const mdxmVertex_t *pVert, const int iWeightNum, float &fTotalWeight, int iNumWeights )
  110. function G2_GetVertWeights(const vert: TGLMVertex): integer;
  111. begin
  112. // Get number of bones per vertex (0..3)+1 = (1..4)
  113. result := (vert.uiNumWeightsAndBoneIndices shr 30) + 1;
  114. end;
  115. function G2_GetVertBoneIndex(const vert: TGLMVertex; iWeightNum: integer): integer;
  116. begin
  117. // Extract the bone reference array index, a 5-bit integer
  118. result := (vert.uiNumWeightsAndBoneIndices shr (5 * iWeightNum)) and 31;
  119. end;
  120. function G2_GetVertBoneWeight(const vert: TGLMVertex; iWeightNum: Cardinal;
  121. var fTotalWeight: single; const iNumWeights: Cardinal): single;
  122. var
  123. fBoneWeight: single;
  124. iTemp: Cardinal;
  125. begin
  126. if (iWeightNum = iNumWeights - 1) then
  127. begin
  128. // No need to calculate final weight value, return the
  129. // weight left over out of 1
  130. fBoneWeight := 1 - fTotalWeight;
  131. end
  132. else
  133. begin
  134. // Get the initial 8-bit bone weight
  135. iTemp := vert.BoneWeightings[iWeightNum];
  136. // Get the 2-bit overflow and 'or' it to the front of the
  137. // weight to get 10-bit integer weight (0..1023)
  138. iTemp := iTemp or ((vert.uiNumWeightsAndBoneIndices shr (12 + (iWeightNum * 2))) and $300);
  139. // Convert to floating point weight (0..1)
  140. fBoneWeight := iTemp / 1023;
  141. // Accumulate total weight
  142. fTotalWeight := fTotalWeight + fBoneWeight;
  143. end;
  144. result := fBoneWeight;
  145. end;
  146. // Adapted from matcomp.c
  147. // void MC_UnCompressQuat(float mat[3][4],const unsigned char * comp)
  148. procedure MC_UnCompressQuat(var mat: TGLMatrix; const comp: TGLACompQuatBone);
  149. begin
  150. mat := QuaternionToMatrix(QuaternionMake([comp[1] - 32726, comp[2] - 32726, comp[3] - 32726],
  151. comp[0] - 32726));
  152. mat.V[3] := VectorMake(comp[4] / 64 - 512, comp[5] / 64 - 512, comp[6] / 64 - 512, 1);
  153. end;
  154. // ------------------
  155. // ------------------ TFileGLM ------------------
  156. // ------------------
  157. procedure TFileGLM.LoadFromStream(aStream: TStream);
  158. var
  159. idstr: array [0 .. 3] of char;
  160. i, j: integer;
  161. ofs, LODofs: int64;
  162. begin
  163. aStream.Read(idstr, sizeof(idstr));
  164. aStream.Position := 0;
  165. if not(idstr = '2LGM') then
  166. begin
  167. raise Exception.Create(Format('Unknown or incorrect identity tag: [%s]', [idstr]));
  168. exit;
  169. end;
  170. aStream.Read(ModelHeader, sizeof(ModelHeader));
  171. if ModelHeader.version <> 6 then
  172. raise Exception.Create(Format('Only GLM (MDXM) version 6 is supported. File is version %d.',
  173. [ModelHeader.version]));
  174. SetLength(SurfaceHeirachyOffsets, ModelHeader.numSurfaces);
  175. aStream.Read(SurfaceHeirachyOffsets[0], sizeof(integer) * ModelHeader.numSurfaces);
  176. SetLength(SurfaceHeirachy, ModelHeader.numSurfaces);
  177. for i := 0 to ModelHeader.numSurfaces - 1 do
  178. with SurfaceHeirachy[i] do
  179. begin
  180. aStream.Read(name, Length(name));
  181. aStream.Read(flags, sizeof(LongWord));
  182. aStream.Read(shader, Length(shader));
  183. aStream.Read(shaderIndex, sizeof(integer));
  184. aStream.Read(parentIndex, sizeof(integer));
  185. aStream.Read(numChildren, sizeof(integer));
  186. if numChildren > 0 then
  187. begin
  188. SetLength(childIndices, numChildren);
  189. aStream.Read(childIndices[0], numChildren * sizeof(integer));
  190. end
  191. else
  192. SetLength(childIndices, 0);
  193. end;
  194. SetLength(LODs, ModelHeader.numLODs);
  195. for i := 0 to ModelHeader.numLODs - 1 do
  196. with LODs[i] do
  197. begin
  198. LODofs := aStream.Position;
  199. aStream.Read(LODInfo, sizeof(LODInfo));
  200. SetLength(LODSurfaceOffsets, ModelHeader.numSurfaces);
  201. aStream.Read(LODSurfaceOffsets[0], sizeof(integer) * ModelHeader.numSurfaces);
  202. SetLength(Surfaces, ModelHeader.numSurfaces);
  203. for j := 0 to ModelHeader.numSurfaces - 1 do
  204. with Surfaces[j] do
  205. begin
  206. ofs := aStream.Position;
  207. aStream.Read(SurfaceHeader, sizeof(TGLMSurfaceHeader));
  208. SetLength(Triangles, SurfaceHeader.numTriangles);
  209. SetLength(Vertices, SurfaceHeader.numVerts);
  210. SetLength(TexCoords, SurfaceHeader.numVerts);
  211. SetLength(BoneReferences, SurfaceHeader.numBoneReferences);
  212. aStream.Position := ofs + SurfaceHeader.ofsTriangles;
  213. aStream.Read(Triangles[0], SurfaceHeader.numTriangles * sizeof(TGLMTriangle));
  214. aStream.Position := ofs + SurfaceHeader.ofsVerts;
  215. aStream.Read(Vertices[0], SurfaceHeader.numVerts * sizeof(TGLMVertex));
  216. aStream.Read(TexCoords[0], SurfaceHeader.numVerts * sizeof(TVector2f));
  217. aStream.Position := ofs + SurfaceHeader.ofsBoneReferences;
  218. aStream.Read(BoneReferences[0], SurfaceHeader.numBoneReferences * sizeof(integer));
  219. aStream.Position := ofs + SurfaceHeader.ofsEnd;
  220. end;
  221. aStream.Position := LODofs + LODInfo.ofsEnd;
  222. end;
  223. end;
  224. // ------------------
  225. // ------------------ TFileGLA ------------------
  226. // ------------------
  227. function TFileGLA.GetCompressedMatrix(Frame, Bone: integer): TGLACompQuatBone;
  228. begin
  229. result := CompBonePool[BoneIndices[Frame * AnimHeader.numBones + Bone]];
  230. end;
  231. // GetUnCompressedMatrix
  232. //
  233. function TFileGLA.GetUnCompressedMatrix(Frame, Bone: integer): TGLMatrix;
  234. begin
  235. MC_UnCompressQuat(result, CompBonePool[BoneIndices[Frame * AnimHeader.numBones + Bone]]);
  236. end;
  237. procedure TFileGLA.LoadFromStream(aStream: TStream);
  238. var
  239. idstr: array [0 .. 3] of char;
  240. i, temp: integer;
  241. buf: array of array [0 .. 2] of Byte;
  242. begin
  243. aStream.Read(idstr, sizeof(idstr));
  244. aStream.Position := 0;
  245. if not(idstr = '2LGA') then
  246. begin
  247. raise Exception.Create(Format('Unknown or incorrect identity tag: [%s]', [idstr]));
  248. exit;
  249. end;
  250. aStream.Read(AnimHeader, sizeof(AnimHeader));
  251. if AnimHeader.version <> 6 then
  252. raise Exception.Create(Format('Only GLA (MDXA) version 6 is supported. File is version %d.',
  253. [AnimHeader.version]));
  254. SetLength(SkeletonOffsets, AnimHeader.numBones);
  255. aStream.Read(SkeletonOffsets[0], sizeof(integer) * AnimHeader.numBones);
  256. SetLength(Skeleton, AnimHeader.numBones);
  257. for i := 0 to AnimHeader.numBones - 1 do
  258. with Skeleton[i] do
  259. begin
  260. aStream.Read(name, Length(name));
  261. aStream.Read(flags, sizeof(LongWord));
  262. aStream.Read(parent, sizeof(integer));
  263. aStream.Read(BasePoseMat, sizeof(TGLABone));
  264. aStream.Read(BasePoseMatInv, sizeof(TGLABone));
  265. aStream.Read(numChildren, sizeof(integer));
  266. if numChildren > 0 then
  267. begin
  268. SetLength(children, numChildren);
  269. aStream.Read(children[0], numChildren * sizeof(integer));
  270. end
  271. else
  272. SetLength(children, 0);
  273. end;
  274. aStream.Position := AnimHeader.ofsFrames;
  275. SetLength(BoneIndices, AnimHeader.numFrames * AnimHeader.numBones);
  276. SetLength(buf, AnimHeader.numFrames * AnimHeader.numBones * 3);
  277. aStream.Read(buf[0], AnimHeader.numFrames * AnimHeader.numBones * 3);
  278. for i := 0 to AnimHeader.numFrames * AnimHeader.numBones - 1 do
  279. BoneIndices[i] := (buf[i][2] shl 16) or (buf[i][1] shl 8) or buf[i][0];
  280. SetLength(buf, 0);
  281. aStream.Position := AnimHeader.ofsCompBonePool;
  282. temp := AnimHeader.ofsEnd - AnimHeader.ofsCompBonePool;
  283. SetLength(CompBonePool, temp div sizeof(TGLACompQuatBone));
  284. aStream.Read(CompBonePool[0], temp);
  285. end;
  286. end.