FileX.pas 17 KB

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