Formatx.X.pas 17 KB

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