Formats.X.pas 17 KB

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