GLS.File3DPDF.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. //
  2. // The graphics engine GLXEngine. The unit of GLScene for Delphi
  3. //
  4. unit GLS.File3DPDF;
  5. (* 3D PDF converter of GLScene's models *)
  6. interface
  7. uses
  8. WinApi.Windows,
  9. WinApi.ShellAPI,
  10. System.Classes,
  11. System.SysUtils,
  12. System.StrUtils,
  13. Stage.VectorTypes,
  14. GLS.PersistentClasses,
  15. Stage.VectorGeometry,
  16. GLS.VectorLists,
  17. GLS.VectorFileObjects,
  18. GLS.ApplicationFileIO,
  19. Stage.Utils;
  20. type
  21. (* The IDTF vector file (Intermediate Data Text File).
  22. Used for converting to IDTF -> U3D -> 3D PDF *)
  23. TGLIDTFVectorFile = class(TGLVectorFile)
  24. private
  25. procedure BuildNormals(m: TGLMeshObject);
  26. public
  27. class function Capabilities: TGLDataFileCapabilities; override;
  28. procedure SaveToStream(aStream: TStream); override;
  29. end;
  30. // The U3D vector file (using IDTF and U3DConverter).
  31. TGLU3DVectorFile = class(TGLIDTFVectorFile)
  32. public
  33. class function Capabilities: TGLDataFileCapabilities; override;
  34. procedure SaveToStream(aStream: TStream); override;
  35. end;
  36. var
  37. // global variable for accing the IDTF->U3D converter
  38. IDTFConverterFileName: string;
  39. //=========================================================
  40. implementation
  41. //=========================================================
  42. const
  43. ConstIDTFTemplate =
  44. ' FILE_FORMAT "IDTF" ' + #13#10 +
  45. ' FORMAT_VERSION 100 ' + #13#10 +
  46. ' ' + #13#10 +
  47. ' NODE "MODEL" { ' + #13#10 +
  48. ' NODE_NAME "VcgMesh01" ' + #13#10 +
  49. ' PARENT_LIST { ' + #13#10 +
  50. ' PARENT_COUNT 1 ' + #13#10 +
  51. ' PARENT 0 { ' + #13#10 +
  52. ' PARENT_NAME "<NULL>" ' + #13#10 +
  53. ' PARENT_TM { ' + #13#10 +
  54. ' 1.000000 0.000000 0.000000 0.000000 ' + #13#10 +
  55. ' 0.000000 1.000000 0.000000 0.000000 ' + #13#10 +
  56. ' 0.000000 0.000000 1.000000 0.000000 ' + #13#10 +
  57. ' 0.000000 0.000000 0.000000 1.000000 ' + #13#10 +
  58. ' } ' + #13#10 +
  59. ' } ' + #13#10 +
  60. ' } ' + #13#10 +
  61. ' RESOURCE_NAME "MyVcgMesh01" ' + #13#10 +
  62. ' } ' + #13#10 +
  63. ' ' + #13#10 +
  64. ' RESOURCE_LIST "MODEL" { ' + #13#10 +
  65. ' RESOURCE_COUNT 1 ' + #13#10 +
  66. ' RESOURCE 0 { ' + #13#10 +
  67. ' RESOURCE_NAME "MyVcgMesh01" ' + #13#10 +
  68. ' MODEL_TYPE "MESH" ' + #13#10 +
  69. ' MESH { ' + #13#10 +
  70. ' FACE_COUNT %d ' + #13#10 +
  71. ' MODEL_POSITION_COUNT %d ' + #13#10 +
  72. ' MODEL_NORMAL_COUNT %d ' + #13#10 +
  73. ' MODEL_DIFFUSE_COLOR_COUNT 0 ' + #13#10 +
  74. ' MODEL_SPECULAR_COLOR_COUNT 0 ' + #13#10 +
  75. ' MODEL_TEXTURE_COORD_COUNT 0 ' + #13#10 +
  76. ' MODEL_BONE_COUNT 0 ' + #13#10 +
  77. ' MODEL_SHADING_COUNT 1 ' + #13#10 +
  78. ' MODEL_SHADING_DESCRIPTION_LIST { ' + #13#10 +
  79. ' SHADING_DESCRIPTION 0 { ' + #13#10 +
  80. ' TEXTURE_LAYER_COUNT 0 ' + #13#10 +
  81. ' SHADER_ID 0 ' + #13#10 +
  82. ' } ' + #13#10 +
  83. ' } ' + #13#10 +
  84. ' MESH_FACE_POSITION_LIST { ' + #13#10 +
  85. ' %s ' + #13#10 +
  86. ' } ' + #13#10 +
  87. ' MESH_FACE_NORMAL_LIST { ' + #13#10 +
  88. ' %s ' + #13#10 +
  89. ' } ' + #13#10 +
  90. ' MESH_FACE_SHADING_LIST { ' + #13#10 +
  91. ' %s ' + #13#10 +
  92. ' } ' + #13#10 +
  93. ' MODEL_POSITION_LIST { ' + #13#10 +
  94. ' %s ' + #13#10 +
  95. ' } ' + #13#10 +
  96. ' MODEL_NORMAL_LIST { ' + #13#10 +
  97. ' %s ' + #13#10 +
  98. ' } ' + #13#10 +
  99. ' } ' + #13#10 +
  100. ' } ' + #13#10 +
  101. ' } ';
  102. var
  103. USFormat: TFormatSettings;
  104. // helper functions
  105. function SingleToStr(const AValue: Single): string; inline;
  106. begin
  107. if AValue = 0.0 then
  108. begin
  109. Result:= '0';
  110. Exit;
  111. end
  112. else
  113. // Limit to maxint
  114. if AValue > (MaxInt - 1) then
  115. Result:= '2147483647'
  116. else
  117. if AValue < -(MaxInt - 1) then
  118. Result:= '2147483647';
  119. Result:= FloatToStrF(AValue, ffFixed, 8, 6, USFormat);
  120. end;
  121. function GetTempPath: string;
  122. var
  123. Len: Integer;
  124. begin
  125. SetLastError(ERROR_SUCCESS);
  126. // get memory for the buffer retaining the temp path (plus null-termination)
  127. SetLength(Result, MAX_PATH);
  128. Len := Winapi.Windows.GetTempPath(MAX_PATH, PChar(Result));
  129. if Len <> 0 then
  130. begin
  131. Len := GetLongPathName(PChar(Result), nil, 0);
  132. GetLongPathName(PChar(Result), PChar(Result), Len);
  133. SetLength(Result, Len - 1);
  134. end
  135. else
  136. Result := '';
  137. end;
  138. procedure ExecProgramAndWait(const AProcessName, AParams: string);
  139. var
  140. startUpInfo : TStartupInfo;
  141. ProcessInfo : TProcessInformation;
  142. exeCmd : string;
  143. ExitCode: cardinal;
  144. begin
  145. // Concat in the parameters
  146. exeCmd := AProcessName + ' ' + AParams;
  147. // Initialise the StartUpInfo record, which handles the creation of
  148. // a new main window for a process
  149. FillChar(startUpInfo, SizeOf(startUpInfo), 0);
  150. StartUpInfo.cb := SizeOf( StartUpInfo );
  151. StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
  152. StartUpInfo.wShowWindow := SW_HIDE;
  153. // Spawn the process out.
  154. if CreateProcess(nil, PChar(exeCmd), nil, nil, false,
  155. CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil,
  156. PChar(ExtractFilePath(AProcessName)), startUpInfo, ProcessInfo) then
  157. begin
  158. // close the thread handle as soon as it is no longer needed
  159. CloseHandle(ProcessInfo.hThread);
  160. // Wait for the process to finish.
  161. if not WaitForSingleObject(ProcessInfo.hProcess, INFINITE) = WAIT_OBJECT_0 then
  162. raise Exception.CreateFmt('U3DConverter: %s', [SysErrorMessage(GetLastError)]);
  163. // finish process
  164. if GetExitCodeProcess(ProcessInfo.hProcess, ExitCode) then
  165. raise Exception.CreateFmt('U3DConverter failed with exitcode $%x', [ExitCode]);
  166. CloseHandle(ProcessInfo.hProcess);
  167. end
  168. else
  169. begin
  170. // create process failure
  171. raise Exception.CreateFmt('U3DConverter: %s', [SysErrorMessage(GetLastError)]);
  172. end;
  173. end;
  174. //=============================
  175. // TGLIDTFVectorFile
  176. //=============================
  177. class function TGLIDTFVectorFile.Capabilities: TGLDataFileCapabilities;
  178. begin
  179. Result := [dfcWrite];
  180. end;
  181. // build normals
  182. procedure TGLIDTFVectorFile.BuildNormals(m: TGLMeshObject);
  183. var
  184. i, j: Integer;
  185. v1, v2, v3, v4, n: TAffineVector;
  186. begin
  187. for i := 0 to m.vertices.count - 1 do
  188. m.Normals.Add(0, 0, 0);
  189. for i := 0 to m.FaceGroups.count - 1 do
  190. if m.FaceGroups[i] is TFGVertexIndexList then
  191. with m.FaceGroups[i] as TFGVertexIndexList do
  192. case mode of
  193. fgmmTriangles:
  194. begin
  195. for j := 0 to (VertexIndices.count div 3) - 1 do
  196. begin
  197. v1 := m.vertices[VertexIndices[j * 3]];
  198. v2 := m.vertices[VertexIndices[j * 3 + 1]];
  199. v3 := m.vertices[VertexIndices[j * 3 + 2]];
  200. n := CalcPlaneNormal(v1, v2, v3);
  201. m.Normals.items[VertexIndices[j * 3]] :=
  202. VectorAdd(m.Normals.items[VertexIndices[j * 3]], n);
  203. m.Normals.items[VertexIndices[j * 3 + 1]] :=
  204. VectorAdd(m.Normals.items[VertexIndices[j * 3 + 1]], n);
  205. m.Normals.items[VertexIndices[j * 3 + 2]] :=
  206. VectorAdd(m.Normals.items[VertexIndices[j * 3 + 2]], n);
  207. end;
  208. end;
  209. fgmmQuads:
  210. begin
  211. for j := 0 to (VertexIndices.count div 4) - 1 do
  212. begin
  213. v1 := m.vertices[VertexIndices[j * 4]];
  214. v2 := m.vertices[VertexIndices[j * 4 + 1]];
  215. v3 := m.vertices[VertexIndices[j * 4 + 2]];
  216. v4 := m.vertices[VertexIndices[j * 4 + 3]];
  217. n := CalcPlaneNormal(v1, v2, v3);
  218. m.Normals.items[VertexIndices[j * 4]] :=
  219. VectorAdd(m.Normals.items[VertexIndices[j * 4]], n);
  220. m.Normals.items[VertexIndices[j * 4 + 1]] :=
  221. VectorAdd(m.Normals.items[VertexIndices[j * 4 + 1]], n);
  222. m.Normals.items[VertexIndices[j * 4 + 2]] :=
  223. VectorAdd(m.Normals.items[VertexIndices[j * 4 + 2]], n);
  224. m.Normals.items[VertexIndices[j * 4 + 3]] :=
  225. VectorAdd(m.Normals.items[VertexIndices[j * 4 + 3]], n);
  226. end;
  227. end;
  228. end;
  229. for i := 0 to m.Normals.count - 1 do
  230. m.Normals.items[i] := VectorNormalize(m.Normals.items[i]);
  231. end;
  232. procedure TGLIDTFVectorFile.SaveToStream(aStream: TStream);
  233. var
  234. S: String;
  235. Mesh: TGLMeshObject;
  236. FaceCount, ModelPositionCount, NormalCount, I, J: Integer;
  237. FacePositionList, NormalList, ModelPositionList, ModelNormalList, FaceShadingList: String;
  238. Lines: TStringList;
  239. Indicies: TGLIntegerList;
  240. function FormatVector(const AVector: TAffineVector): string;
  241. begin
  242. Result:= SingleToStr(AVector.X) + ' ' + SingleToStr(AVector.Y) + ' ' + SingleToStr(AVector.Z);
  243. end;
  244. begin
  245. // ++ todo: save more than one mesh
  246. Mesh:= Owner.MeshObjects[0];
  247. // BuildNormals(Mesh);
  248. // count
  249. NormalList:= '';
  250. ModelNormalList:= '';
  251. // faces
  252. // MESH_FACE_POSITION_LIST
  253. FaceCount:= 0;
  254. FacePositionList:= '';
  255. for i := 0 to Mesh.FaceGroups.count - 1 do
  256. if Mesh.FaceGroups[i] is TFGVertexIndexList then
  257. with Mesh.FaceGroups[i] as TFGVertexIndexList do
  258. begin
  259. // face indicies
  260. Indicies:= TFGVertexIndexList(Mesh.FaceGroups[i]).VertexIndices;
  261. Inc(FaceCount, Indicies.Count div 3);
  262. J:= 0;
  263. while J < Indicies.Count do
  264. begin
  265. FacePositionList:= FacePositionList + ' ' + Format('%d %d %d', [Indicies[J +2], Indicies[J +1], Indicies[J]]) + #13#10;
  266. inc(J, 3);
  267. end;
  268. end;
  269. FacePositionList:= Trim(FacePositionList);
  270. FaceShadingList:= '';
  271. for i := 1 to FaceCount do
  272. FaceShadingList:= FaceShadingList + ' 0' + #13#10;
  273. FaceShadingList:= Trim(FaceShadingList);
  274. // verticies
  275. ModelPositionList:= '';
  276. ModelPositionCount:= Mesh.Vertices.Count;
  277. for I:= 0 to Pred(Mesh.Vertices.Count) do
  278. ModelPositionList:= ModelPositionList + ' ' + FormatVector(Mesh.Vertices[I]) + #13#10;
  279. ModelPositionList:= Trim(ModelPositionList);
  280. // points
  281. // normals
  282. // FMeshObject.BuildNormals(FMeshObject.Vertices., momFaceGroups); ++
  283. NormalCount:= Mesh.Normals.Count;
  284. NormalList:= '';
  285. for I:= 0 to Pred(Mesh.Normals.Count) do
  286. NormalList:= NormalList + ' ' + FormatVector(Mesh.Normals[I]) + #13#10;
  287. NormalList:= Trim(NormalList);
  288. // build the IDTF file
  289. S:= Format(ConstIDTFTemplate, [FaceCount, ModelPositionCount, NormalCount,
  290. FacePositionList, NormalList, FaceShadingList, ModelPositionList, ModelNormalList]);
  291. ;
  292. Lines:= TStringList.Create;
  293. try
  294. Lines.Text:= S;
  295. Lines.SaveToStream(aStream, TEncoding.ASCII);
  296. finally
  297. Lines.Free;
  298. end;
  299. end;
  300. //==================================
  301. // TGLU3DVectorFile
  302. //==================================
  303. class function TGLU3DVectorFile.Capabilities: TGLDataFileCapabilities;
  304. begin
  305. Result := [dfcWrite];
  306. end;
  307. procedure TGLU3DVectorFile.SaveToStream(aStream: TStream);
  308. var
  309. TempStream: TStream;
  310. TempInFile, TempOutFile, Params: String;
  311. begin
  312. TempInFile:= IncludeTrailingPathDelimiter(GetTempPath) + 'GLObject.idtf';
  313. // save as temp .idtf file
  314. TempStream:= TFileStream.Create(TempInFile, fmCreate);
  315. try
  316. inherited SaveToStream(TempStream);
  317. finally
  318. TempStream.Free;
  319. end;
  320. // convert IDTF to U3D using U3DConverter
  321. TempOutFile:= ChangeFileExt(TempInFile, '.u3d');
  322. Params:= Format('-i "%s" -o "%s"', [TempInFile, TempOutFile]);
  323. ExecProgramAndWait(IDTFConverterFileName, Params);
  324. // copy U3D file to stream
  325. TempStream:= TFileStream.Create(TempOutFile, fmOpenRead or fmShareDenyWrite);
  326. try
  327. AStream.CopyFrom(TempStream, 0);
  328. finally
  329. TempStream.Free;
  330. end;
  331. DeleteFile(TempInFile);
  332. DeleteFile(TempOutFile);
  333. end;
  334. // ------------------------------------------------------------------
  335. initialization
  336. // ------------------------------------------------------------------
  337. USFormat:= TFormatSettings.Create('en_us');
  338. IDTFConverterFileName:= IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'IDTFConverter.exe';
  339. // register formats
  340. RegisterVectorFileFormat('idtf', 'Intermediate Data Text File', TGLIDTFVectorFile);
  341. RegisterVectorFileFormat('u3d', 'Universal 3D', TGLU3DVectorFile);
  342. end.