FileX.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. (*
  5. Simple X format support for Delphi (Microsoft's favorite format)
  6. *)
  7. unit FileX;
  8. interface
  9. {$I GLScene.inc}
  10. uses
  11. System.Classes,
  12. System.SysUtils,
  13. GLVectorTypes,
  14. GLVectorGeometry,
  15. GLVectorLists,
  16. GLPersistentClasses,
  17. GLUtils;
  18. type
  19. TDXNode = class;
  20. TDXFileHeader = record
  21. Magic : array[0..3] of AnsiChar;
  22. Major : array[0..1] of AnsiChar;
  23. Minor : array[0..1] of AnsiChar;
  24. FileType : array[0..3] of AnsiChar;
  25. FloatType : array[0..3] of AnsiChar;
  26. end;
  27. TDXNode = class (TList)
  28. private
  29. FName,
  30. FTypeName : String;
  31. FOwner : TDXNode;
  32. function GetItem(index : Integer) : TDXNode;
  33. public
  34. constructor CreateOwned(AOwner : TDXNode);
  35. constructor Create; virtual;
  36. procedure Clear; override;
  37. property Name : String read FName write FName;
  38. property TypeName : String read FTypeName write FTypeName;
  39. property Owner : TDXNode read FOwner;
  40. property Items[index : Integer] : TDXNode read GetItem;
  41. end;
  42. TDXMaterialList = class;
  43. TDXMaterial = class (TPersistentObject)
  44. private
  45. FDiffuse : TVector4f;
  46. FSpecPower : Single;
  47. FSpecular,
  48. FEmissive : TVector3f;
  49. FTexture : String;
  50. public
  51. constructor CreateOwned(AOwner : TDXMaterialList);
  52. property Diffuse : TVector4f read FDiffuse write FDiffuse;
  53. property SpecPower : Single read FSpecPower write FSpecPower;
  54. property Specular : TVector3f read FSpecular write FSpecular;
  55. property Emissive : TVector3f read FEmissive write FEmissive;
  56. property Texture : String read FTexture write FTexture;
  57. end;
  58. TDXMaterialList = class (TDXNode)
  59. private
  60. function GetMaterial(index : Integer) : TDXMaterial;
  61. public
  62. property Items[index : Integer] : TDXMaterial read GetMaterial;
  63. end;
  64. TDXFrame = class (TDXNode)
  65. private
  66. FMatrix : TMatrix;
  67. public
  68. constructor Create; override;
  69. function GlobalMatrix : TMatrix;
  70. property Matrix : TMatrix read FMatrix write FMatrix;
  71. end;
  72. TDXMesh = class (TDXNode)
  73. private
  74. FVertices,
  75. FNormals,
  76. FTexCoords : TAffineVectorList;
  77. FVertexIndices,
  78. FNormalIndices,
  79. FMaterialIndices,
  80. FVertCountIndices : TIntegerList;
  81. FMaterialList : TDXMaterialList;
  82. public
  83. constructor Create; override;
  84. destructor Destroy; override;
  85. property Vertices : TAffineVectorList read FVertices;
  86. property Normals : TAffineVectorList read FNormals;
  87. property TexCoords : TAffineVectorList read FTexCoords;
  88. property VertexIndices : TIntegerList read FVertexIndices;
  89. property NormalIndices : TIntegerList read FNormalIndices;
  90. property MaterialIndices : TIntegerList read FMaterialIndices;
  91. property VertCountIndices : TIntegerList read FVertCountIndices;
  92. property MaterialList : TDXMaterialList read FMaterialList;
  93. end;
  94. TDXFile = class
  95. private
  96. FRootNode : TDXNode;
  97. FHeader : TDXFileHeader;
  98. protected
  99. procedure ParseText(Stream : TStream);
  100. procedure ParseBinary(Stream : TStream);
  101. public
  102. constructor Create;
  103. destructor Destroy; override;
  104. procedure LoadFromStream(Stream : TStream);
  105. //procedure SaveToStream(Stream : TStream);
  106. property Header : TDXFileHeader read FHeader;
  107. property RootNode : TDXNode read FRootNode;
  108. end;
  109. // ----------------------------------------------------------------------
  110. implementation
  111. // ----------------------------------------------------------------------
  112. // ----------------------------------------------------------------------
  113. // Text parsing functions
  114. // ----------------------------------------------------------------------
  115. procedure RemoveComments(Text : TStringList);
  116. var
  117. i, comment : Integer;
  118. begin
  119. for i:=0 to Text.Count-1 do begin
  120. comment:=Pos('//',Text[i]);
  121. if comment>0 then
  122. Text[i]:=Copy(Text[i], 0, comment-1);
  123. comment:=Pos('#',Text[i]);
  124. if comment>0 then
  125. Text[i]:=Copy(Text[i], 0, comment-1);
  126. end;
  127. end;
  128. // ----------------------------------------------------------------------
  129. // TDXFile
  130. // ----------------------------------------------------------------------
  131. constructor TDXFile.Create;
  132. begin
  133. FRootNode:=TDXNode.Create;
  134. end;
  135. destructor TDXFile.Destroy;
  136. begin
  137. FRootNode.Free;
  138. inherited;
  139. end;
  140. procedure TDXFile.LoadFromStream(Stream : TStream);
  141. begin
  142. Stream.Read(FHeader, SizeOf(TDXFileHeader));
  143. Assert(Header.Magic = 'xof ', 'Invalid DirectX file');
  144. if Header.FileType = 'txt ' then
  145. ParseText(Stream)
  146. else
  147. if Header.FileType = 'bin ' then
  148. raise Exception.Create('FileX error, "bin" filetype not supported')
  149. else
  150. if Header.FileType = 'tzip' then
  151. raise Exception.Create('FileX error, "tzip" filetype not supported')
  152. else
  153. if Header.FileType = 'bzip' then
  154. raise Exception.Create('FileX error, "bzip" filetype not supported');
  155. end;
  156. procedure TDXFile.ParseBinary(Stream: TStream);
  157. begin
  158. // To-do
  159. end;
  160. procedure TDXFile.ParseText(Stream: TStream);
  161. var
  162. XText,
  163. TempBuffer : TStringList;
  164. Cursor : Integer;
  165. Buffer : String;
  166. function ContainsColon(const Buffer : String) : Boolean;
  167. begin
  168. Result:=Pos(';', Buffer)>0;
  169. end;
  170. function ContainsBegin(const Buffer : String) : Boolean;
  171. begin
  172. Result:=Pos('{', Buffer)>0;
  173. end;
  174. function ContainsEnd(const Buffer : String) : Boolean;
  175. begin
  176. Result:=Pos('}', Buffer)>0;
  177. end;
  178. function ReadString : String;
  179. begin
  180. if Cursor<XText.Count then
  181. Result:=XText[Cursor]
  182. else
  183. Result:='';
  184. Inc(Cursor);
  185. end;
  186. function GetNodeData(var NodeType, NodeName : String) : Boolean;
  187. begin
  188. NodeType:='';
  189. NodeName:='';
  190. Result:=False;
  191. if Cursor<3 then exit;
  192. NodeType:=XText[Cursor-3];
  193. NodeName:=XText[Cursor-2];
  194. if ContainsBegin(NodeType)
  195. or ContainsEnd(NodeType)
  196. or ContainsColon(NodeType) then begin
  197. NodeType:=NodeName;
  198. NodeName:='';
  199. end;
  200. NodeType:=LowerCase(NodeType);
  201. end;
  202. function ReadInteger : Integer;
  203. var
  204. str : String;
  205. begin
  206. str:=ReadString;
  207. if ContainsColon(str) then str:=StringReplace(str, ';', '', [rfReplaceAll]);
  208. if ContainsBegin(str) then str:=StringReplace(str, '{', '', [rfReplaceAll]);
  209. if ContainsEnd(str) then str:=StringReplace(str, '}', '', [rfReplaceAll]);
  210. Result:=StrToInt(str);
  211. end;
  212. function ReadSingle : Single;
  213. var
  214. str : String;
  215. begin
  216. str:=ReadString;
  217. if ContainsColon(str) then str:=StringReplace(str, ';', '', [rfReplaceAll]);
  218. if ContainsBegin(str) then str:=StringReplace(str, '{', '', [rfReplaceAll]);
  219. if ContainsEnd(str) then str:=StringReplace(str, '}', '', [rfReplaceAll]);
  220. Result:=StrToFloatDef(str);
  221. end;
  222. function ReadMatrix : TMatrix;
  223. var
  224. i, j : Integer;
  225. begin
  226. try
  227. for j:=0 to 3 do
  228. for i:=0 to 3 do
  229. Result.V[i].V[j]:=ReadSingle;
  230. except
  231. on E:Exception do begin
  232. Result:=IdentityHMGMatrix;
  233. end;
  234. end;
  235. end;
  236. function ReadVector3f : TAffineVector;
  237. var
  238. str : String;
  239. begin
  240. str:=ReadString;
  241. str:=StringReplace(str, ';', ' ', [rfReplaceAll]);
  242. TempBuffer.CommaText:=str;
  243. if TempBuffer.Count > 1 then begin
  244. Result.X:=StrToFloatDef(TempBuffer[0]);
  245. Result.Y:=StrToFloatDef(TempBuffer[1]);
  246. Result.Z:=StrToFloatDef(TempBuffer[2]);
  247. end else begin
  248. Result.X:=StrToFloatDef(TempBuffer[0]);
  249. Result.Y:=ReadSingle;
  250. Result.Z:=ReadSingle;
  251. end;
  252. end;
  253. function ReadVector4f : TVector;
  254. var
  255. str : String;
  256. begin
  257. str:=ReadString;
  258. str:=StringReplace(str, ';', ' ', [rfReplaceAll]);
  259. TempBuffer.CommaText:=str;
  260. if TempBuffer.Count > 1 then begin
  261. Result.X:=StrToFloatDef(TempBuffer[0]);
  262. Result.Y:=StrToFloatDef(TempBuffer[1]);
  263. Result.Z:=StrToFloatDef(TempBuffer[2]);
  264. Result.W:=StrToFloatDef(TempBuffer[3]);
  265. end else begin
  266. Result.X:=StrToFloatDef(TempBuffer[0]);
  267. Result.Y:=ReadSingle;
  268. Result.Z:=ReadSingle;
  269. Result.W:=ReadSingle;
  270. end;
  271. end;
  272. function ReadTexCoord : TAffineVector;
  273. var
  274. str : String;
  275. begin
  276. str:=ReadString;
  277. str:=StringReplace(str, ';', ' ', [rfReplaceAll]);
  278. TempBuffer.CommaText:=str;
  279. if TempBuffer.Count > 1 then begin
  280. Result.X:=StrToFloatDef(TempBuffer[0]);
  281. Result.Y:=StrToFloatDef(TempBuffer[1]);
  282. end else begin
  283. Result.X:=StrToFloatDef(TempBuffer[0]);
  284. Result.Y:=ReadSingle;
  285. end;
  286. Result.Z:=0;
  287. end;
  288. procedure ReadMeshVectors(VectorList : TAffineVectorList);
  289. var
  290. i, NumVectors : Integer;
  291. begin
  292. NumVectors:=ReadInteger;
  293. for i:=0 to NumVectors-1 do
  294. VectorList.Add(ReadVector3f);
  295. end;
  296. procedure ReadMeshIndices(IndexList : TIntegerList; VertCountIndices : TIntegerList = nil);
  297. var
  298. str : String;
  299. i, j, NumFaces, NumIndices, jStart : Integer;
  300. Indices : array of Integer;
  301. begin
  302. NumFaces:=ReadInteger;
  303. for i:=0 to NumFaces-1 do begin
  304. str:=ReadString;
  305. str:=StringReplace(str, ';', ' ', [rfReplaceAll]);
  306. TempBuffer.CommaText:=str;
  307. NumIndices:=StrToInt(TempBuffer[0]);
  308. SetLength(Indices, NumIndices);
  309. jStart:=0;
  310. if TempBuffer.Count>1 then begin
  311. Indices[0]:=StrToInt(TempBuffer[1]);
  312. jStart:=1;
  313. end;
  314. for j:=jStart to NumIndices-1 do
  315. Indices[j]:=ReadInteger;
  316. case NumIndices of
  317. 3 : begin
  318. IndexList.Add(Indices[0], Indices[1], Indices[2]);
  319. if Assigned(VertCountIndices) then VertCountIndices.Add(3);
  320. end;
  321. 4 : begin
  322. IndexList.Add(Indices[0], Indices[1], Indices[2]);
  323. IndexList.Add(Indices[0], Indices[2], Indices[3]);
  324. if Assigned(VertCountIndices) then VertCountIndices.Add(6);
  325. end;
  326. end;
  327. SetLength(Indices, 0);
  328. end;
  329. end;
  330. procedure ReadTexCoords(VectorList : TAffineVectorList);
  331. var
  332. i, NumVectors : Integer;
  333. begin
  334. NumVectors:=ReadInteger;
  335. for i:=0 to NumVectors-1 do
  336. VectorList.Add(ReadTexCoord);
  337. end;
  338. procedure ReadMeshVertices(Mesh : TDXMesh);
  339. begin
  340. ReadMeshVectors(Mesh.Vertices);
  341. ReadMeshIndices(Mesh.VertexIndices, Mesh.VertCountIndices);
  342. end;
  343. procedure ReadMeshNormals(Mesh : TDXMesh);
  344. begin
  345. ReadMeshVectors(Mesh.Normals);
  346. ReadMeshIndices(Mesh.NormalIndices);
  347. end;
  348. procedure ReadMeshTexCoords(Mesh : TDXMesh);
  349. begin
  350. ReadTexCoords(Mesh.TexCoords);
  351. end;
  352. procedure ReadMeshMaterialList(Mesh : TDXMesh);
  353. var
  354. i, {NumMaterials,} NumIndices : Integer;
  355. begin
  356. {NumMaterials:=}ReadInteger;
  357. NumIndices:=ReadInteger;
  358. for i:=0 to NumIndices-1 do
  359. Mesh.MaterialIndices.Add(ReadInteger);
  360. end;
  361. procedure ReadMeshMaterial(Mesh : TDXMesh);
  362. begin
  363. with TDXMaterial.CreateOwned(Mesh.MaterialList) do begin
  364. Diffuse:=ReadVector4f;
  365. SpecPower:=ReadSingle;
  366. Specular:=ReadVector3f;
  367. Emissive:=ReadVector3f;
  368. end;
  369. end;
  370. procedure ReadTextureFilename(Mesh : TDXMesh);
  371. var
  372. Str : String;
  373. begin
  374. if Mesh.MaterialList.Count>0 then begin
  375. Str:=ReadString;
  376. Str:=StringReplace(Str, '"', '', [rfReplaceAll]);
  377. Str:=StringReplace(Str, ';', '', [rfReplaceAll]);
  378. Str:=Trim(Str);
  379. Mesh.MaterialList.Items[Mesh.MaterialList.Count-1].Texture:=Str;
  380. end;
  381. end;
  382. procedure ReadStruct(ParentNode : TDXNode);
  383. var
  384. Buffer,
  385. NodeType,
  386. NodeName : String;
  387. Loop : Boolean;
  388. NewNode : TDXNode;
  389. begin
  390. Loop:=True;
  391. while Loop do begin
  392. Buffer:=ReadString;
  393. if Cursor>XText.Count-1 then break;
  394. if ContainsEnd(Buffer) then
  395. Loop:=False
  396. else if ContainsBegin(Buffer) then begin
  397. GetNodeData(NodeType, NodeName);
  398. NewNode:=nil;
  399. // Frame
  400. if NodeType = 'frame' then begin
  401. NewNode:=TDXFrame.CreateOwned(ParentNode);
  402. ReadStruct(NewNode);
  403. // Frame transform matrix
  404. end else if NodeType = 'frametransformmatrix' then begin
  405. if ParentNode is TDXFrame then
  406. TDXFrame(ParentNode).Matrix:=ReadMatrix;
  407. ReadStruct(ParentNode);
  408. // Mesh
  409. end else if NodeType = 'mesh' then begin
  410. NewNode:=TDXMesh.CreateOwned(ParentNode);
  411. ReadMeshVertices(TDXMesh(NewNode));
  412. ReadStruct(NewNode);
  413. // Mesh normals
  414. end else if NodeType = 'meshnormals' then begin
  415. if ParentNode is TDXMesh then
  416. ReadMeshNormals(TDXMesh(ParentNode));
  417. ReadStruct(ParentNode);
  418. // Mesh texture coords
  419. end else if NodeType = 'meshtexturecoords' then begin
  420. if ParentNode is TDXMesh then
  421. ReadMeshTexCoords(TDXMesh(ParentNode));
  422. ReadStruct(ParentNode);
  423. // Mesh material list
  424. end else if NodeType = 'meshmateriallist' then begin
  425. if ParentNode is TDXMesh then
  426. ReadMeshMaterialList(TDXMesh(ParentNode));
  427. ReadStruct(ParentNode);
  428. // Mesh material
  429. end else if NodeType = 'material' then begin
  430. if ParentNode is TDXMesh then
  431. ReadMeshMaterial(TDXMesh(ParentNode));
  432. ReadStruct(ParentNode);
  433. // Material texture filename
  434. end else if NodeType = 'texturefilename' then begin
  435. if ParentNode is TDXMesh then
  436. ReadTextureFilename(TDXMesh(ParentNode));
  437. ReadStruct(ParentNode);
  438. // Unknown type
  439. end else begin
  440. //NewNode:=TDXNode.CreateOwned(ParentNode);
  441. //NodeType:='*'+NodeType;
  442. //ReadStruct(NewNode);
  443. ReadStruct(ParentNode);
  444. end;
  445. if Assigned(NewNode) then begin
  446. NewNode.TypeName:=NodeType;
  447. NewNode.Name:=NodeName;
  448. end;
  449. end;
  450. end;
  451. end;
  452. begin
  453. XText:=TStringList.Create;
  454. TempBuffer:=TStringList.Create;
  455. XText.LoadFromStream(Stream);
  456. // Remove comments and white spaces
  457. RemoveComments(XText);
  458. XText.CommaText:=XText.Text;
  459. // Fix embedded open braces
  460. Cursor:=0;
  461. while Cursor<XText.Count-1 do begin
  462. Buffer:=ReadString;
  463. if Pos('{',Buffer)>1 then begin
  464. XText[Cursor-1]:=Copy(Buffer,0,Pos('{',Buffer)-1);
  465. XText.Insert(Cursor,'{');
  466. end;
  467. end;
  468. XText.SaveToFile('XText_dump.txt');
  469. // Start parsing
  470. Cursor:=0;
  471. while Cursor<XText.Count-1 do
  472. ReadStruct(RootNode);
  473. TempBuffer.Free;
  474. XText.Free;
  475. end;
  476. // ----------------------------------------------------------------------
  477. // TDXMaterialList
  478. // ----------------------------------------------------------------------
  479. function TDXMaterialList.GetMaterial(index: Integer): TDXMaterial;
  480. begin
  481. Result:=TDXMaterial(Get(index));
  482. end;
  483. // ----------------------------------------------------------------------
  484. // TDXMesh
  485. // ----------------------------------------------------------------------
  486. constructor TDXMesh.Create;
  487. begin
  488. inherited;
  489. FVertices:=TAffineVectorList.Create;
  490. FNormals:=TAffineVectorList.Create;
  491. FTexCoords:=TAffineVectorList.Create;
  492. FVertexIndices:=TIntegerList.Create;
  493. FNormalIndices:=TIntegerList.Create;
  494. FMaterialIndices:=TIntegerList.Create;
  495. FVertCountIndices:=TIntegerList.Create;
  496. FMaterialList:=TDXMaterialList.Create;
  497. end;
  498. destructor TDXMesh.Destroy;
  499. begin
  500. FVertices.Free;
  501. FNormals.Free;
  502. FTexCoords.Free;
  503. FVertexIndices.Free;
  504. FNormalIndices.Free;
  505. FMaterialIndices.Free;
  506. FVertCountIndices.Free;
  507. FMaterialList.Free;
  508. inherited;
  509. end;
  510. // ----------------------------------------------------------------------
  511. // TDXNode
  512. // ----------------------------------------------------------------------
  513. constructor TDXNode.Create;
  514. begin
  515. // Virtual
  516. end;
  517. constructor TDXNode.CreateOwned(AOwner: TDXNode);
  518. begin
  519. FOwner:=AOwner;
  520. Create;
  521. if Assigned(FOwner) then
  522. FOwner.Add(Self);
  523. end;
  524. function TDXNode.GetItem(index: Integer): TDXNode;
  525. begin
  526. Result:=TDXNode(Get(index));
  527. end;
  528. procedure TDXNode.Clear;
  529. var
  530. i : integer;
  531. begin
  532. for i:=0 to Count-1 do
  533. Items[i].Free;
  534. inherited;
  535. end;
  536. // ----------------------------------------------------------------------
  537. // TDXFrame
  538. // ----------------------------------------------------------------------
  539. constructor TDXFrame.Create;
  540. begin
  541. inherited;
  542. FMatrix:=IdentityHMGMatrix;
  543. end;
  544. function TDXFrame.GlobalMatrix: TMatrix;
  545. begin
  546. if Owner is TDXFrame then
  547. Result:=MatrixMultiply(TDXFrame(Owner).GlobalMatrix, FMatrix)
  548. else
  549. Result:=FMatrix;
  550. end;
  551. // ----------------------------------------------------------------------
  552. // TDXMaterial
  553. // ----------------------------------------------------------------------
  554. constructor TDXMaterial.CreateOwned(AOwner: TDXMaterialList);
  555. begin
  556. Create;
  557. if Assigned(AOwner) then
  558. AOwner.Add(Self);
  559. end;
  560. end.