2
0

GXS.FileMDC.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.FileMDC;
  5. (*
  6. Code for loading animated MDC files into FreeForms
  7. and Actors.
  8. This file format uses in Return To Castle Wolfenstein instead
  9. of MD3 files. It has got all MD3 features (such as Tag frames)
  10. plus very small data!
  11. *)
  12. interface
  13. {$I Stage.Defines.inc}
  14. uses
  15. System.Classes,
  16. System.SysUtils,
  17. GXS.VectorFileObjects,
  18. GXS.Material,
  19. GXS.ApplicationFileIO,
  20. Stage.VectorGeometry;
  21. const
  22. MDCFILE_IDENTITY = 'IDPC';
  23. MDCFILE_VERSION = 2;
  24. MDC_BASEVERTEX_FACTOR = 0.015625; // 1/64;
  25. MDC_COMPVERTEX_FACTOR = 0.046875; // 3/64;
  26. type
  27. TMDCPoint = array [0 .. 2] of Single;
  28. TMDCAngle = TMDCPoint;
  29. TMDCFileHeader = packed record
  30. Ident: array [0 .. 3] of AnsiChar;
  31. Version: Cardinal;
  32. Name: array [0 .. 63] of AnsiChar;
  33. Flags: Cardinal;
  34. NumFrames: Cardinal;
  35. NumTags: Cardinal;
  36. NumSurfaces: Cardinal;
  37. NumSkins: Cardinal;
  38. OffsetBorderFrames: Cardinal;
  39. OffsetTagNames: Cardinal;
  40. OffsetTagFrames: Cardinal;
  41. OffsetSurfaces: Cardinal;
  42. OffsetEnd: Cardinal;
  43. end;
  44. TMDCBorderFrame = packed record
  45. BBMin, BBMax: TMDCPoint;
  46. LocalOrigin: TMDCPoint;
  47. Radius: Single;
  48. Name: array [0 .. 15] of AnsiChar;
  49. end;
  50. PMDCTagName = ^TMDCTagName;
  51. TMDCTagName = packed record
  52. Name: array [0 .. 63] of AnsiChar;
  53. end;
  54. PMDCTagFrame = ^TMDCTagFrame;
  55. TMDCTagFrame = packed record
  56. TagPosition: array [0 .. 2] of Word; // or ShortInt?
  57. TagAngle: array [0 .. 2] of Word; // or ShortInt?
  58. end;
  59. TMDCTag = packed record
  60. TagName: PMDCTagName;
  61. TagFrame: PMDCTagFrame;
  62. end;
  63. TMDCSurfaceHeader = packed record
  64. Ident: array [0 .. 3] of AnsiChar;
  65. Name: array [0 .. 63] of AnsiChar;
  66. Flags: Cardinal;
  67. NumCompFrames: Cardinal;
  68. NumBaseFrames: Cardinal;
  69. NumSkins: Cardinal;
  70. NumVertices: Cardinal;
  71. NumTriangles: Cardinal;
  72. OffsetTriangles: Cardinal;
  73. OffsetSkins: Cardinal;
  74. OffsetTexCoords: Cardinal;
  75. OffsetBaseVerts: Cardinal;
  76. OffsetCompVerts: Cardinal;
  77. OffsetFrameBaseFrames: Cardinal;
  78. OffsetFrameCompFrames: Cardinal;
  79. OffsetEnd: Cardinal;
  80. end;
  81. TMDCTriangle = array [0 .. 2] of Cardinal;
  82. TMDCSkin = packed record
  83. Shader: array [0 .. 63] of AnsiChar;
  84. Flags: Cardinal;
  85. end;
  86. TMDCTexCoord = array [0 .. 1] of Single;
  87. TMDCBaseVertex = array [0 .. 3] of SmallInt;
  88. TMDCCompVertex = array [0 .. 3] of Byte;
  89. TMDCBaseFrame = packed record
  90. BaseVertices: array of TMDCBaseVertex;
  91. end;
  92. TMDCCompFrame = packed record
  93. CompVertices: array of TMDCCompVertex;
  94. end;
  95. type
  96. TgxMDCVectorFile = class(TgxVectorFile)
  97. public
  98. class function Capabilities: TDataFileCapabilities; override;
  99. procedure LoadFromStream(AStream: TStream); override;
  100. end;
  101. // ------------------------------------------------------------------
  102. implementation
  103. // ------------------------------------------------------------------
  104. // ------------------
  105. // ------------------ TgxMDCVectorFile ------------------
  106. // ------------------
  107. class function TgxMDCVectorFile.Capabilities: TDataFileCapabilities;
  108. begin
  109. Result := [DfcRead];
  110. end;
  111. procedure TgxMDCVectorFile.LoadFromStream(AStream: TStream);
  112. type
  113. PPackedNormal = ^TPackedNormal;
  114. TPackedNormal = array [0 .. 1] of Byte;
  115. var
  116. I, J, K, NumVerts, Numtris: Integer;
  117. Mesh: TgxMorphableMeshObject;
  118. FaceGroup: TgxFGIndexTexCoordList;
  119. MorphTarget: TgxMeshMorphTarget;
  120. function UnpackNormal(Pn: TPackedNormal): TAffineVector;
  121. var
  122. Lat, Lng: Single;
  123. begin
  124. // The MDC normal is a latitude/longitude value that needs
  125. // to be calculated into cartesian space.
  126. Lat := (Pn[0]) * (2 * Pi) / 255;
  127. Lng := (Pn[1]) * (2 * Pi) / 255;
  128. Result.X := Cos(Lat) * Sin(Lng);
  129. Result.Y := Sin(Lat) * Sin(Lng);
  130. Result.Z := Cos(Lng);
  131. end;
  132. procedure AllocateMaterial(Meshname: string);
  133. var
  134. LibMat: TgxLibMaterial;
  135. begin
  136. // If a material library is assigned to the actor/freeform the
  137. // mesh name will be added as a material.
  138. if Assigned(Owner.MaterialLibrary) then
  139. with Owner.MaterialLibrary do
  140. begin
  141. if Assigned(Materials.GetLibMaterialByName(Meshname)) then
  142. Exit;
  143. LibMat := Materials.Add;
  144. LibMat.Name := Meshname;
  145. LibMat.Material.Texture.Disabled := False;
  146. end;
  147. end;
  148. var
  149. Fileheader: TMDCFileHeader;
  150. Surfheader: TMDCSurfaceHeader;
  151. Borderframes: array of TMDCBorderFrame;
  152. Baseframetable, Compframetable: array of Word;
  153. Baseframe: TMDCBaseFrame;
  154. Compframe: TMDCCompFrame;
  155. Xyz, Normal: TAffineVector;
  156. St: array of array [0 .. 1] of Single;
  157. Triangles: array of TMDCTriangle;
  158. FrameOffset: Cardinal;
  159. begin
  160. AStream.Read(Fileheader, SizeOf(Fileheader));
  161. Assert(Fileheader.Ident = MDCFILE_IDENTITY, 'Incorrect MDC file Ident');
  162. Assert(Fileheader.Version = MDCFILE_VERSION, 'Incorrect MDC version number');
  163. try
  164. AStream.Seek(Fileheader.OffsetBorderFrames, SoFromBeginning);
  165. SetLength(Borderframes, Fileheader.NumFrames);
  166. AStream.Read(Borderframes[0], SizeOf(TMDCBorderFrame) *
  167. Fileheader.NumFrames);
  168. FrameOffset := Fileheader.OffsetSurfaces;
  169. for I := 0 to Fileheader.NumSurfaces - 1 do
  170. begin
  171. // read header
  172. AStream.Position := FrameOffset;
  173. AStream.Read(Surfheader, SizeOf(TMDCSurfaceHeader));
  174. // triangles for this surface
  175. SetLength(Triangles, Surfheader.NumTriangles);
  176. AStream.Position := FrameOffset + Surfheader.OffsetTriangles;
  177. AStream.Read(Triangles[0], SizeOf(TMDCTriangle) *
  178. Surfheader.NumTriangles);
  179. // texture coordinates for this surface
  180. SetLength(St, Surfheader.NumVertices);
  181. AStream.Position := FrameOffset + Surfheader.OffsetTexCoords;
  182. AStream.Read(St[0], 2 * SizeOf(Single) * Surfheader.NumVertices);
  183. // base frame table for this surface (for only loading)
  184. SetLength(Baseframetable, Fileheader.NumFrames);
  185. AStream.Position := FrameOffset + Surfheader.OffsetFrameBaseFrames;
  186. AStream.Read(Baseframetable[0], SizeOf(Word) * Fileheader.NumFrames);
  187. // compressed frame table for this surface (for only loading)
  188. SetLength(Compframetable, Fileheader.NumFrames);
  189. AStream.Position := FrameOffset + Surfheader.OffsetFrameCompFrames;
  190. AStream.Read(Compframetable[0], SizeOf(Word) * Fileheader.NumFrames);
  191. Mesh := TgxMorphableMeshObject.CreateOwned(Owner.MeshObjects);
  192. // easiest way to convert a char array to string ;)
  193. Mesh.Name := Trim(string(PChar(Surfheader.Name[0])));
  194. with Mesh do
  195. begin
  196. Mode := MomFaceGroups;
  197. FaceGroup := TgxFGIndexTexCoordList.CreateOwned(FaceGroups);
  198. with FaceGroup do
  199. begin
  200. AllocateMaterial(Mesh.Name);
  201. MaterialName := Mesh.Name;
  202. NumTris := Surfheader.NumTriangles;
  203. VertexIndices.Capacity := NumTris * 3;
  204. TexCoords.Capacity := NumTris * 3;
  205. // Get the vertex indices and texture coordinates
  206. for J := 0 to Surfheader.NumTriangles - 1 do
  207. begin
  208. Add(Triangles[J, 0], St[Triangles[J, 0]][0],
  209. 1 - St[Triangles[J, 0]][1]);
  210. Add(Triangles[J, 2], St[Triangles[J, 2]][0],
  211. 1 - St[Triangles[J, 2]][1]);
  212. Add(Triangles[J, 1], St[Triangles[J, 1]][0],
  213. 1 - St[Triangles[J, 1]][1]);
  214. end;
  215. end;
  216. // Get the mesh data for each morph frame
  217. for J := 0 to Fileheader.NumFrames - 1 do
  218. begin
  219. MorphTarget := TgxMeshMorphTarget.CreateOwned(MorphTargets);
  220. MorphTarget.Name := Trim(string(Pchar(Surfheader.Name[0]))) + '[' +
  221. IntToStr(J) + ']';
  222. NumVerts := Surfheader.NumVertices;
  223. MorphTarget.Vertices.Capacity := NumVerts;
  224. // base frames
  225. SetLength(Baseframe.BaseVertices, Surfheader.NumVertices);
  226. AStream.Seek(FrameOffset + Surfheader.OffsetBaseVerts + Baseframetable
  227. [J] * Surfheader.NumVertices * 8, SoFromBeginning);
  228. AStream.Read(Baseframe.BaseVertices[0], SizeOf(TMDCBaseVertex) *
  229. Surfheader.NumVertices);
  230. // compressed frames
  231. if Compframetable[J] <> $FFFF then // is there a valid frame?
  232. begin
  233. SetLength(Compframe.CompVertices, Surfheader.NumVertices);
  234. AStream.Seek(FrameOffset + Surfheader.OffsetCompVerts +
  235. Compframetable[J] * Surfheader.NumVertices * 4, SoFromBeginning);
  236. AStream.Read(Compframe.CompVertices[0], SizeOf(TMDCCompVertex) *
  237. Surfheader.NumVertices);
  238. end;
  239. for K := 0 to Surfheader.NumVertices - 1 do
  240. begin
  241. Xyz.X :=
  242. (Baseframe.BaseVertices[K, 0] * MDC_BASEVERTEX_FACTOR) +
  243. Borderframes[J].Localorigin[0];
  244. Xyz.Y :=
  245. (Baseframe.BaseVertices[K, 1] * MDC_BASEVERTEX_FACTOR) +
  246. Borderframes[J].Localorigin[1];
  247. Xyz.Z :=
  248. (Baseframe.BaseVertices[K, 2] * MDC_BASEVERTEX_FACTOR) +
  249. Borderframes[J].Localorigin[2];
  250. Normal := UnpackNormal
  251. (PPackedNormal(@Baseframe.BaseVertices[K, 3])^);
  252. if Compframetable[J] <> $FFFF then
  253. begin
  254. Xyz.X := Xyz.X +
  255. ((Compframe.CompVertices[K, 0] - 128) * MDC_COMPVERTEX_FACTOR);
  256. Xyz.Y := Xyz.Y +
  257. ((Compframe.CompVertices[K, 1] - 128) * MDC_COMPVERTEX_FACTOR);
  258. Xyz.Z := Xyz.Z +
  259. ((Compframe.CompVertices[K, 2] - 128) * MDC_COMPVERTEX_FACTOR);
  260. // FIXME:
  261. // I'm sure compframe.CompVertices[3] points a packed normal.
  262. // And it must be add the current normal like xyz.
  263. // But, I don't know a way to unpacked this value
  264. // I found a precalculated normal list in RTCW 1.41 mod source (q_math.c)
  265. //
  266. // NUMVERTEXNORMALS = 162
  267. // vec3_t bytedirs[NUMVERTEXNORMALS] = {
  268. // {-0.525731, 0.000000, 0.850651}, (...)
  269. //
  270. // But, I had noticed some compframe.CompVertices[3] value is bigger
  271. // than NUMVERTEXNORMALS constant. So, there must be another list.
  272. // Can you find it?
  273. // Osman Turan ([email protected])
  274. end;
  275. // all id Sofware based games uses Z axis as up instead of Y. So, convert them
  276. MorphTarget.Vertices.Add(Xyz.X, Xyz.Z, -Xyz.Y);
  277. MorphTarget.Normals.Add(Normal.X, Normal.Z,
  278. -Normal.Y);
  279. end;
  280. end;
  281. end;
  282. FrameOffset := FrameOffset + Surfheader.OffsetEnd;
  283. if Mesh.MorphTargets.Count > 0 then
  284. Mesh.MorphTo(0);
  285. end;
  286. finally
  287. // save memory free space
  288. Borderframes := nil;
  289. Baseframetable := nil;
  290. Compframetable := nil;
  291. St := nil;
  292. Triangles := nil;
  293. end;
  294. end;
  295. // ------------------------------------------------------------------
  296. initialization
  297. // ------------------------------------------------------------------
  298. RegisterVectorFileFormat('mdc', 'MDC files', TgxMDCVectorFile);
  299. end.