GLS.FileOBJ.pas 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.FileOBJ;
  5. (*
  6. Loading Wavefront OBJ Files into FreeForms
  7. Note that you must manually add this unit to one of your project's uses
  8. to enable support for OBJ & OBJF at run-time.
  9. *)
  10. interface
  11. {$I Stage.Defines.inc}
  12. {.$DEFINE STATS} // Define to display statistics after loading.
  13. uses
  14. Winapi.OpenGL,
  15. Winapi.OpenGLext,
  16. System.Classes,
  17. System.SysUtils,
  18. Stage.VectorTypes,
  19. GLS.ApplicationFileIO,
  20. GLS.PersistentClasses,
  21. Stage.VectorGeometry,
  22. GLS.Scene,
  23. GLS.VectorFileObjects,
  24. GLS.VectorLists,
  25. GLS.Texture,
  26. GLS.Color,
  27. GLS.RenderContextInfo,
  28. GLS.Material,
  29. Stage.Utils;
  30. const
  31. // Load input data in chunks of BufSize Bytes.
  32. BufSize = 10240;
  33. // Allocate memory for the current line in chunks of LineLen Bytes.
  34. LineLen = 100;
  35. type
  36. TGLOBJVectorFile = class(TGLVectorFile)
  37. private
  38. FSourceStream: TStream; // Load from this stream
  39. FBuffer: AnsiString; // Buffer
  40. FLine: string; // current line
  41. FLineNo: Integer; // current Line number - for error messages
  42. FEof: Boolean; // Stream done?
  43. FBufPos: Integer; // Position in the buffer
  44. protected
  45. // Read a single line of text from the source stream, set FEof to true when done.
  46. procedure ReadLine;
  47. // Raise a class-specific exception
  48. procedure Error(const msg: string);
  49. procedure CalcMissingOBJNormals(mesh: TGLMeshObject);
  50. public
  51. class function Capabilities: TGLDataFileCapabilities; override;
  52. procedure LoadFromStream(aStream: TStream); override;
  53. procedure SaveToStream(aStream: TStream); override;
  54. end;
  55. EGLOBJFileError = class(Exception)
  56. private
  57. FLineNo: Integer;
  58. public
  59. property LineNo: Integer read FLineNo;
  60. end;
  61. (*A simple class that know how to extract infos from a mtl file.
  62. mtl files are companion files of the obj, they store material
  63. information. Guessed content (imported ones denoted with a '*',
  64. please help if you know more):
  65. materials begin with a 'newmtl' command followed by material name
  66. *Kd command defines the diffuse color
  67. *map_Kd command defines the diffuse texture image
  68. *Ka/map_Ka define the ambient color and texmap
  69. *Ks/map_Ks define the specular color and texmap
  70. *Ke/map_Ke define the self-illumination/lightmap texmap
  71. map_Bump specifies the bump map
  72. *d specifies transparency (alpha-channel, range [0; 1])
  73. map_d specifies the opcaity texture map
  74. Ns defines the specular exponent or shininess or phong specular (?)
  75. Ni is the refraction index (greater than 1)
  76. *illum defines the illumination model (0 for no lighting, 1 for
  77. ambient and diffuse, 2 for full lighting) *)
  78. TGLMTLFile = class(TStringList)
  79. public
  80. procedure Prepare;
  81. function MaterialStringProperty(const materialName, propertyName: string): string;
  82. function MaterialVectorProperty(const materialName, propertyName: string;
  83. const defaultValue: TGLVector): TGLVector;
  84. end;
  85. var
  86. (* If enabled, main mesh will be splitted into multiple mesh from facegroup data.*)
  87. vGLFileOBJ_SplitMesh: boolean = False;
  88. // ------------------------------------------------------------------
  89. implementation
  90. // ------------------------------------------------------------------
  91. uses
  92. Stage.Strings,
  93. Stage.OpenGLTokens,
  94. GLS.XOpenGL,
  95. GLS.Context,
  96. GLS.MeshUtils;
  97. function StreamEOF(S: TStream): Boolean;
  98. begin
  99. // Is the stream at its end?
  100. Result := (S.Position >= S.Size);
  101. end;
  102. function Rest(const s: string; Count: integer): string;
  103. // Return the right part of s including s[Count].
  104. begin
  105. Result := copy(s, Count, Length(s) - Count + 1);
  106. end;
  107. // Return the next Delimiter-delimited Token from the string s and remove it from s
  108. function NextToken(var s: string; delimiter: Char): string;
  109. var
  110. p: Integer;
  111. begin
  112. p := Pos(Delimiter, s);
  113. if p = 0 then
  114. begin
  115. Result := s;
  116. s := '';
  117. end
  118. else
  119. begin
  120. Result := copy(s, 1, p - 1);
  121. s := TrimLeft(Rest(s, p + 1));
  122. end;
  123. end;
  124. (* ** TOBJFGVertexNormalTexIndexList ******************************************
  125. - adds support for polygons and for "missing" normals and
  126. texture-coordinates. Pass -1 to Add for the index of a missing object.
  127. - Polygons are defined by counting off the number of vertices added to the
  128. PolygonVertices-property. So a PolygonVertices-List of
  129. [3,4,6]
  130. says "Vertex indices 0,1 and 2 make up a triangle, 3,4,5 and 6 a quad and
  131. 7,8,9,10,11 and 12 a hexagon".
  132. *)
  133. type
  134. TOBJFGMode = (objfgmmPolygons, objfgmmTriangleStrip);
  135. TOBJFGVertexNormalTexIndexList = class(TFGVertexNormalTexIndexList)
  136. private
  137. FMode: TOBJFGMode;
  138. FName: string;
  139. FPolygonVertices: TGLIntegerList;
  140. FCurrentVertexCount: integer;
  141. FShowNormals: boolean;
  142. procedure PolygonComplete; (* Current polygon completed. Adds FCurrentVertexCount
  143. to FPolygonVertices and sets the variable to 0 *)
  144. procedure SetMode(aMode: TOBJFGMode);
  145. public
  146. procedure Assign(Source: TPersistent); override;
  147. constructor CreateOwned(aOwner: TglFaceGroups); override;
  148. destructor Destroy; override;
  149. procedure WriteToFiler(writer: TGLVirtualWriter); override;
  150. procedure ReadFromFiler(reader: TGLVirtualReader); override;
  151. procedure Add(VertexIdx, NormalIdx, TexCoordIdx: Integer);
  152. procedure BuildList(var mrci: TGLRenderContextInfo); override;
  153. procedure AddToTriangles(aList: TGLAffineVectorList;
  154. aTexCoords: TGLAffineVectorList = nil;
  155. aNormals: TGLAffineVectorList = nil); override;
  156. function TriangleCount: Integer; override;
  157. property Mode: TOBJFGMode read FMode write SetMode;
  158. property Name: string read FName write FName;
  159. property PolygonVertices: TGLIntegerList read FPolygonVertices;
  160. property ShowNormals: boolean read FShowNormals write FShowNormals;
  161. end;
  162. constructor TOBJFGVertexNormalTexIndexList.CreateOwned(aOwner: TGLFaceGroups);
  163. begin
  164. inherited CreateOwned(aOwner);
  165. FMode := objfgmmTriangleStrip;
  166. //FShowNormals:=True;
  167. end;
  168. destructor TOBJFGVertexNormalTexIndexList.Destroy;
  169. begin
  170. FPolygonVertices.Free;
  171. inherited Destroy;
  172. end;
  173. procedure TOBJFGVertexNormalTexIndexList.Add(VertexIdx, NormalIdx, TexCoordIdx: Integer);
  174. begin
  175. inherited Add(VertexIdx, NormalIdx, TexCoordIdx);
  176. inc(FCurrentVertexCount);
  177. end;
  178. procedure TOBJFGVertexNormalTexIndexList.PolygonComplete;
  179. begin
  180. Assert(FMode = objfgmmPolygons, 'PolygonComplete may only be called for Facegroups with Mode=objfgmmPolygons.');
  181. FPolygonVertices.Add(FCurrentVertexCount);
  182. FCurrentVertexCount := 0;
  183. end;
  184. procedure TOBJFGVertexNormalTexIndexList.SetMode(aMode: TOBJFGMode);
  185. begin
  186. if aMode = FMode then
  187. exit;
  188. Assert(VertexIndices.Count = 0, 'Decide on the mode before adding vertices.');
  189. FMode := aMode;
  190. if FMode = objfgmmPolygons then
  191. FPolygonVertices := TGLIntegerList.Create
  192. else
  193. begin
  194. FPolygonVertices.Free;
  195. FPolygonVertices := nil;
  196. end;
  197. end;
  198. procedure TOBJFGVertexNormalTexIndexList.BuildList(var mrci: TGLRenderContextInfo);
  199. var
  200. VertexPool: PAffineVectorArray;
  201. NormalPool: PAffineVectorArray;
  202. TexCoordPool: PAffineVectorArray;
  203. ColorPool: PVectorArray;
  204. GotColor: Boolean;
  205. procedure BuildPolygons;
  206. var
  207. Polygon, Index, j, idx: Integer;
  208. N: TAffineVector;
  209. begin
  210. // Build it. Ignoring texture-coordinates and normals that are missing.
  211. Index := 0; // Current index into the Index-Lists.
  212. // For every Polygon
  213. for Polygon := 0 to FPolygonVertices.Count - 1 do
  214. begin
  215. gl.Begin_(GL_POLYGON);
  216. try
  217. // For every Vertex in the current Polygon
  218. for j := 0 to FPolygonVertices[Polygon] - 1 do
  219. begin
  220. Assert(NormalIndices.List <> nil);
  221. idx := NormalIndices.List^[Index];
  222. if idx >= 0 then
  223. gl.Normal3fv(@NormalPool[idx]);
  224. if GotColor then
  225. gl.Color4fv(@ColorPool[VertexIndices.List^[Index]]);
  226. if Assigned(TexCoordPool) then
  227. begin
  228. idx := TexCoordIndices.List^[Index];
  229. if idx >= 0 then
  230. begin
  231. if gl.ARB_multitexture and (not xgl.SecondTextureUnitForbidden) then
  232. begin
  233. gl.MultiTexCoord2fv(GL_TEXTURE0, @TexCoordPool[idx]);
  234. gl.MultiTexCoord2fv(GL_TEXTURE1, @TexCoordPool[idx]);
  235. end
  236. else
  237. begin
  238. gl.TexCoord2fv(@TexCoordPool[idx]);
  239. end;
  240. end;
  241. end;
  242. gl.Vertex3fv(@VertexPool[VertexIndices.List^[Index]]);
  243. Inc(Index);
  244. end;
  245. finally
  246. gl.End_;
  247. end;
  248. end;
  249. // Visible normals, rather moronic and mainly for debugging.
  250. if FShowNormals then
  251. begin
  252. Index := 0;
  253. for Polygon := 0 to FPolygonVertices.Count - 1 do
  254. begin
  255. // For every Vertex in the current Polygon
  256. for j := 0 to FPolygonVertices[Polygon] - 1 do
  257. begin
  258. idx := NormalIndices.List^[Index];
  259. if idx <> -1 then
  260. begin
  261. gl.Begin_(GL_LINES);
  262. try
  263. gl.Vertex3fv(@VertexPool^[VertexIndices.List^[Index]]);
  264. N := VectorAdd(VertexPool^[VertexIndices.List^[Index]], VectorScale(NormalPool^[idx], 0.1));
  265. gl.Vertex3fv(@N);
  266. finally
  267. gl.End_;
  268. end;
  269. end;
  270. inc(Index);
  271. end;
  272. end;
  273. end;
  274. end;
  275. procedure BuildTriangleStrip;
  276. (*
  277. begin
  278. Owner.Owner.DeclareArraysToOpenGL(False);
  279. gl.DrawElements(GL_TRIANGLE_STRIP,VertexIndices.Count,
  280. GL_UNSIGNED_INT,VertexIndices.List);
  281. end;
  282. *)
  283. var
  284. Index, idx: Integer;
  285. begin
  286. // Build it. Ignoring texture-coordinates and normals that are missing.
  287. gl.Begin_(GL_TRIANGLE_STRIP);
  288. try
  289. for Index := 0 to VertexIndices.Count - 1 do
  290. begin
  291. idx := NormalIndices.List^[Index];
  292. if idx <> -1 then
  293. gl.Normal3fv(@NormalPool^[idx]);
  294. if Assigned(TexCoordPool) then
  295. begin
  296. idx := TexCoordIndices.List^[Index];
  297. if idx <> -1 then
  298. xgl.TexCoord2fv(@TexCoordPool^[idx]);
  299. end;
  300. gl.Vertex3fv(@VertexPool^[VertexIndices.List^[Index]]);
  301. end;
  302. finally
  303. gl.End_;
  304. end;
  305. end;
  306. begin
  307. Assert(((TexCoordIndices.Count = 0) or (VertexIndices.Count <= TexCoordIndices.Count))
  308. and (VertexIndices.Count <= NormalIndices.Count),
  309. 'Number of Vertices does not match number of Normals or Texture coordinates.');
  310. // Shorthand notations.
  311. VertexPool := Owner.Owner.Vertices.List;
  312. NormalPool := Owner.Owner.Normals.List;
  313. ColorPool := Owner.Owner.Colors.List;
  314. if TexCoordIndices.Count = 0 then
  315. TexCoordPool := nil
  316. else
  317. TexCoordPool := Owner.Owner.TexCoords.List;
  318. GotColor := (Owner.Owner.Vertices.Count = Owner.Owner.Colors.Count);
  319. case FMode of
  320. objfgmmPolygons: BuildPolygons;
  321. objfgmmTriangleStrip: BuildTriangleStrip;
  322. end;
  323. end;
  324. procedure TOBJFGVertexNormalTexIndexList.AddToTriangles(aList: TGLAffineVectorList;
  325. aTexCoords: TGLAffineVectorList = nil;
  326. aNormals: TGLAffineVectorList = nil);
  327. var
  328. i, j, n, n0: Integer;
  329. vertexList, texCoordList, normalsList: TGLAffineVectorList;
  330. begin
  331. vertexList := Owner.Owner.Vertices;
  332. texCoordList := Owner.Owner.TexCoords;
  333. normalsList := Owner.Owner.Normals;
  334. case FMode of
  335. objfgmmPolygons:
  336. begin
  337. n := 0;
  338. for i := 0 to FPolygonVertices.Count - 1 do
  339. begin
  340. n0 := n;
  341. for j := 0 to FPolygonVertices[i] - 1 do
  342. begin
  343. if j > 1 then
  344. begin
  345. aList.Add(vertexList[VertexIndices[n0]],
  346. vertexList[VertexIndices[n - 1]],
  347. vertexList[VertexIndices[n]]);
  348. if Assigned(aTexCoords) then
  349. begin
  350. if texCoordList.Count > 0 then
  351. aTexCoords.Add(texCoordList[VertexIndices[n0]],
  352. texCoordList[VertexIndices[n - 1]],
  353. texCoordList[VertexIndices[n]])
  354. else
  355. aTexCoords.AddNulls(3);
  356. end;
  357. if Assigned(aNormals) then
  358. begin
  359. if normalsList.Count > 0 then
  360. aNormals.Add(normalsList[VertexIndices[n0]],
  361. normalsList[VertexIndices[n - 1]],
  362. normalsList[VertexIndices[n]])
  363. else
  364. aNormals.AddNulls(3);
  365. end;
  366. end;
  367. Inc(n);
  368. end;
  369. end;
  370. end;
  371. objfgmmTriangleStrip:
  372. begin
  373. ConvertStripToList(vertexList, VertexIndices, aList);
  374. n := (VertexIndices.Count - 2) * 3;
  375. if Assigned(aTexCoords) then
  376. begin
  377. if texCoordList.Count > 0 then
  378. ConvertStripToList(texCoordList, VertexIndices, aTexCoords)
  379. else
  380. aTexCoords.AddNulls(n);
  381. end;
  382. if Assigned(aNormals) then
  383. begin
  384. if normalsList.Count > 0 then
  385. ConvertStripToList(normalsList, VertexIndices, aNormals)
  386. else
  387. aNormals.AddNulls(n);
  388. end;
  389. end;
  390. else
  391. Assert(False);
  392. end;
  393. end;
  394. function TOBJFGVertexNormalTexIndexList.TriangleCount: Integer;
  395. var
  396. i: Integer;
  397. begin
  398. case FMode of
  399. objfgmmPolygons:
  400. begin
  401. Result := 0;
  402. for i := 0 to FPolygonVertices.Count - 1 do
  403. begin
  404. Result := Result + FPolygonVertices[i] - 2;
  405. end;
  406. end;
  407. objfgmmTriangleStrip:
  408. begin
  409. Result := VertexIndices.Count - 2;
  410. if Result < 0 then
  411. Result := 0;
  412. end;
  413. else
  414. Result := 0;
  415. Assert(False);
  416. end;
  417. end;
  418. // ------------------
  419. // ------------------ TGLOBJVectorFile ------------------
  420. // ------------------
  421. procedure TGLOBJVectorFile.ReadLine;
  422. var
  423. j: Integer;
  424. procedure FillBuffer;
  425. var
  426. l: Integer;
  427. begin
  428. l := FSourceStream.Size - FSourceStream.Position;
  429. if l > BufSize then
  430. l := BufSize;
  431. SetLength(FBuffer, l);
  432. FSourceStream.Read(FBuffer[1], l);
  433. FBufPos := 1;
  434. end;
  435. begin
  436. Inc(FLineNo);
  437. if FBufPos < 1 then
  438. FillBuffer;
  439. j := 1;
  440. while True do
  441. begin
  442. if FBufPos > Length(FBuffer) then
  443. begin
  444. if StreamEof(FSourceStream) then
  445. begin
  446. FEof := True;
  447. break;
  448. end
  449. else
  450. FillBuffer
  451. end
  452. else
  453. begin
  454. case FBuffer[FBufPos] of
  455. #10, #13:
  456. begin
  457. Inc(FBufPos);
  458. if FBufPos > Length(FBuffer) then
  459. if StreamEof(FSourceStream) then
  460. break
  461. else
  462. FillBuffer;
  463. if (FBuffer[FBufPos] = #10) or (FBuffer[FBufPos] = #13) then
  464. Inc(FBufPos);
  465. break;
  466. end;
  467. else
  468. if j > Length(FLine) then
  469. SetLength(FLine, Length(FLine) + LineLen);
  470. if FBuffer[FBufPos] = #9 then
  471. FLine[j] := #32
  472. else
  473. FLine[j] := Char(FBuffer[FBufPos]);
  474. Inc(FBufPos);
  475. Inc(j);
  476. end;
  477. end;
  478. end;
  479. SetLength(FLine, j - 1);
  480. end;
  481. procedure TGLOBJVectorFile.Error(const msg: string);
  482. var
  483. E: EGLOBJFileError;
  484. begin
  485. E := EGLOBJFileError.Create(Msg);
  486. E.FLineNo := FLineNo;
  487. raise E;
  488. end;
  489. class function TGLOBJVectorFile.Capabilities: TGLDataFileCapabilities;
  490. begin
  491. Result := [dfcRead, dfcWrite];
  492. end;
  493. procedure TGLOBJVectorFile.CalcMissingOBJNormals(mesh: TGLMeshObject);
  494. var
  495. vertexPool: PAffineVectorArray;
  496. n: TAffineVector;
  497. p: array[1..3] of PAffineVector;
  498. face, index: Integer;
  499. fg: TOBJFGVertexNormalTexIndexList;
  500. procedure DoCalcNormal;
  501. var
  502. idx: Integer;
  503. begin
  504. idx := TOBJFGVertexNormalTexIndexList(Mesh.FaceGroups[Face]).NormalIndices.List^[Index];
  505. if idx < 0 then
  506. begin
  507. n := CalcPlaneNormal(p[1]^, p[2]^, p[3]^);
  508. idx := Mesh.Normals.Add(n);
  509. TOBJFGVertexNormalTexIndexList(Mesh.FaceGroups[Face]).NormalIndices.List^[Index] := idx;
  510. end;
  511. end;
  512. procedure CalcForPolygons;
  513. var
  514. polygon, firstVertexIndex, j: Integer;
  515. begin
  516. with FG do
  517. begin
  518. // Walk the polygons and calculate normals for those vertices that are missing
  519. Index := 0; // Current index into the Index-List of this Facegroup.
  520. // For every Polygon
  521. for Polygon := 0 to FPolygonVertices.Count - 1 do
  522. begin
  523. // Init
  524. FirstVertexIndex := Index;
  525. FillChar(p, SizeOf(p), 0);
  526. // Last Vertex in this polygon
  527. p[2] := @VertexPool^[VertexIndices.List^[Index + FPolygonVertices[Polygon] - 1]];
  528. // First Vertex in this polygon
  529. p[3] := @VertexPool^[VertexIndices.List^[Index]];
  530. // For every Vertex in the current Polygon but the last.
  531. for j := 0 to FPolygonVertices[Polygon] - 2 do
  532. begin
  533. Move(p[2], p[1], 2 * SizeOf(PAffineVector));
  534. p[3] := @VertexPool^[VertexIndices.List^[Index + 1]];
  535. DoCalcNormal;
  536. Inc(Index);
  537. end;
  538. // For the last vertex use the first as partner to span the plane.
  539. Move(p[2], p[1], 2 * SizeOf(PAffineVector));
  540. p[3] := @VertexPool^[VertexIndices.List^[FirstVertexIndex]];
  541. DoCalcNormal;
  542. inc(Index);
  543. end; // of for FPolygonVertices
  544. end; // of with Facegroup
  545. end;
  546. procedure CalcForTriangleStrip;
  547. begin
  548. end;
  549. begin
  550. // Shorthand notations.
  551. VertexPool := Mesh.Vertices.List;
  552. for Face := 0 to Mesh.FaceGroups.Count - 1 do
  553. begin
  554. FG := TOBJFGVertexNormalTexIndexList(Mesh.FaceGroups[Face]);
  555. case FG.Mode of
  556. objfgmmPolygons: CalcForPolygons;
  557. objfgmmTriangleStrip: CalcForTriangleStrip;
  558. end;
  559. end;
  560. end;
  561. procedure TGLOBJVectorFile.LoadFromStream(aStream: TStream);
  562. var
  563. hv: THomogeneousVector;
  564. av: TAffineVector;
  565. mesh: TGLMeshObject;
  566. faceGroup: TOBJFGVertexNormalTexIndexList;
  567. faceGroupNames: TStringList;
  568. // Read a vector with a maximum of 4 elements from the current line.
  569. procedure ReadHomogeneousVector;
  570. var
  571. i, c: Integer;
  572. f: string;
  573. begin
  574. FillChar(hv, SizeOf(hv), 0);
  575. i := 0;
  576. while (FLine <> '') and (i < 4) do
  577. begin
  578. f := NextToken(FLine, ' ');
  579. Val(f, hv.V[i], c);
  580. if c <> 0 then
  581. Error(Format('''%s'' is not a valid floating-point constant.', [f]));
  582. Inc(i);
  583. end;
  584. end;
  585. // Read a vector with a maximum of 3 elements from the current line.
  586. procedure ReadAffineVector;
  587. var
  588. i, c: integer;
  589. f: string;
  590. begin
  591. FillChar(av, SizeOf(av), 0);
  592. i := 0;
  593. while (FLine <> '') and (i < 3) do
  594. begin
  595. f := NextToken(FLine, ' ');
  596. Val(f, av.V[i], c);
  597. if c <> 0 then
  598. Error(Format('''%s'' is not a valid floating-point constant.', [f]));
  599. inc(i);
  600. end;
  601. end;
  602. procedure SetCurrentFaceGroup(aName: string; const matlName: string);
  603. var
  604. i: Integer;
  605. begin
  606. i := faceGroupNames.IndexOf(aName);
  607. if i >= 0 then
  608. begin
  609. faceGroup := TOBJFGVertexNormalTexIndexList(faceGroupNames.Objects[i]);
  610. if faceGroup.MaterialName <> matlName then
  611. begin
  612. i := faceGroupNames.IndexOf(aName);
  613. if i >= 0 then
  614. begin
  615. faceGroup := TOBJFGVertexNormalTexIndexList(faceGroupNames.Objects[i]);
  616. if faceGroup.MaterialName <> matlName then
  617. faceGroup := nil;
  618. end;
  619. end;
  620. end;
  621. if (faceGroup = nil) or (faceGroup.Name <> aName)
  622. or (faceGroup.PolygonVertices.Count > 0)
  623. or (faceGroup.MaterialName <> matlName) then
  624. begin
  625. faceGroup := TOBJFGVertexNormalTexIndexList.CreateOwned(Mesh.FaceGroups);
  626. faceGroup.FName := aName;
  627. faceGroup.Mode := objfgmmPolygons;
  628. faceGroup.MaterialName := matlName;
  629. faceGroupNames.AddObject(aName, faceGroup);
  630. end;
  631. end;
  632. procedure AddFaceVertex(faceVertices: string);
  633. function GetIndex(Count: Integer): Integer;
  634. var
  635. s: string;
  636. begin
  637. s := NextToken(FaceVertices, '/');
  638. Result := StrToIntDef(s, 0);
  639. if Result = 0 then
  640. Result := -1 // Missing
  641. else if Result < 0 then
  642. begin
  643. // Relative, make absolute. "-1" means last, "-2" second last.
  644. Result := Count + Result
  645. end
  646. else
  647. begin
  648. // Absolute, correct for zero-base.
  649. Dec(Result);
  650. end;
  651. end;
  652. var
  653. vIdx, tIdx, nIdx: Integer;
  654. begin
  655. vIdx := GetIndex(mesh.Vertices.Count);
  656. tIdx := GetIndex(mesh.TexCoords.Count);
  657. nIdx := GetIndex(mesh.Normals.Count);
  658. faceGroup.Add(vIdx, nIdx, tIdx);
  659. end;
  660. procedure ReadFace(const curMtlName: string);
  661. var
  662. faceVertices: string;
  663. begin
  664. if FLine <> '' then
  665. begin
  666. if not Assigned(FaceGroup) then
  667. SetCurrentFaceGroup('', curMtlName);
  668. try
  669. while FLine <> '' do
  670. begin
  671. faceVertices := NextToken(FLine, ' ');
  672. AddFaceVertex(faceVertices);
  673. end;
  674. finally
  675. FaceGroup.PolygonComplete;
  676. end;
  677. end;
  678. end;
  679. procedure ReadTriangleStripContinued;
  680. var
  681. faceVertices: string;
  682. begin
  683. if faceGroup = nil then
  684. Error('q-line without preceding t-line.');
  685. while FLine <> '' do
  686. begin
  687. FaceVertices := NextToken(FLine, ' ');
  688. AddFaceVertex(FaceVertices);
  689. end;
  690. end;
  691. procedure ReadTriangleStrip;
  692. begin
  693. // Start a new Facegroup, mode=triangle strip
  694. faceGroup := TOBJFGVertexNormalTexIndexList.CreateOwned(Mesh.FaceGroups);
  695. faceGroup.Mode := objfgmmTriangleStrip;
  696. // The rest is the same as for continuation of a strip.
  697. ReadTriangleStripContinued;
  698. end;
  699. function GetOrAllocateMaterial(const libName, matName: string): string;
  700. var
  701. fs: TStream;
  702. objMtl: TGLMTLFile;
  703. matLib: TGLMaterialLibrary;
  704. libMat, libMat2: TGLLibMaterial;
  705. texName: string;
  706. libFilename: string;
  707. begin
  708. if GetOwner is TGLBaseMesh then
  709. begin
  710. // got a linked material library?
  711. matLib := TGLBaseMesh(GetOwner).MaterialLibrary;
  712. if Assigned(matLib) then
  713. begin
  714. Result := matName;
  715. libMat := matLib.Materials.GetLibMaterialByName(matName);
  716. if not Assigned(libMat) then
  717. begin
  718. // spawn a new material
  719. libMat := matLib.Materials.Add;
  720. libMat.Name := matName;
  721. // get full path for material file to be load
  722. if matLib.TexturePaths = EmptyStr then
  723. libFilename := libName
  724. else
  725. libFilename := IncludeTrailingPathDelimiter(matLib.TexturePaths) + libName;
  726. try
  727. fs := TFileStream.Create(libFilename,fmOpenRead);
  728. except
  729. fs := nil;
  730. end;
  731. if Assigned(fs) then
  732. begin
  733. objMtl := TGLMTLFile.Create;
  734. try
  735. objMtl.LoadFromStream(fs);
  736. objMtl.Prepare;
  737. // setup material colors
  738. with libMat.Material.FrontProperties do
  739. begin
  740. Ambient.Color := objMtl.MaterialVectorProperty(matName, 'Ka', clrGray20);
  741. Diffuse.Color := objMtl.MaterialVectorProperty(matName, 'Kd', clrGray80);
  742. Diffuse.Alpha := GLStrToFloatDef(objMtl.MaterialStringProperty(matName, 'd'), 1);
  743. if Diffuse.Alpha < 1 then
  744. libMat.Material.BlendingMode := bmTransparency;
  745. case StrToIntDef(objMtl.MaterialStringProperty(matName, 'illum'), 1) of
  746. 0:
  747. begin // non-lit material
  748. libMat.Material.MaterialOptions := [moNoLighting];
  749. end;
  750. 1: ; // flat, non-shiny material
  751. 2:
  752. begin // specular material
  753. Specular.Color := objMtl.MaterialVectorProperty(matName, 'Ks', clrTransparent);
  754. end;
  755. else
  756. // unknown, assume unlit
  757. libMat.Material.MaterialOptions := [moNoLighting];
  758. Diffuse.Color := clrGray80;
  759. end;
  760. Shininess := StrToIntDef(objMtl.MaterialStringProperty(matName, 'Ns'), 1);
  761. end;
  762. // setup texture
  763. texName := objMtl.MaterialStringProperty(matName, 'map_Kd');
  764. if texName <> '' then
  765. begin
  766. try
  767. with libMat.Material.Texture do
  768. begin
  769. Image.LoadFromFile(texName);
  770. Disabled := False;
  771. TextureMode := tmModulate;
  772. end;
  773. except
  774. on E: ETexture do
  775. begin
  776. if not Owner.IgnoreMissingTextures then
  777. raise;
  778. end;
  779. end;
  780. end;
  781. // setup lightmap (self-illumination) texture
  782. texName := objMtl.MaterialStringProperty(matName, 'map_Ke');
  783. if texName <> '' then
  784. begin
  785. // spawn a new material
  786. libMat2 := matLib.Materials.Add;
  787. libMat2.Name := matName + '_lm';
  788. // Use the GLScene built-in second texture support (note: the mesh LightmapProperty MUST be empty)
  789. libMat.Texture2Name := libMat2.Name;
  790. try
  791. with libMat2.Material.Texture do
  792. begin
  793. Image.LoadFromFile(texName);
  794. Disabled := False;
  795. minFilter := miLinear;
  796. TextureWrap := twNone;
  797. TextureFormat := tfRGB;
  798. TextureMode := tmModulate;
  799. end;
  800. except
  801. on E: ETexture do
  802. begin
  803. if not Owner.IgnoreMissingTextures then
  804. raise;
  805. end;
  806. end;
  807. end;
  808. finally
  809. objMtl.Free;
  810. fs.Free;
  811. end;
  812. end;
  813. end
  814. else
  815. Result := matName;
  816. end
  817. else
  818. Result := '';
  819. end;
  820. end;
  821. procedure SplitMesh;
  822. var
  823. i, j, count: Integer;
  824. newMesh: TGLMeshObject;
  825. newfaceGroup: TOBJFGVertexNormalTexIndexList;
  826. VertexIdx, NormalIdx, TexCoordIdx: Integer;
  827. AffineVector: TAffineVector;
  828. begin
  829. for i := 0 to mesh.FaceGroups.Count-1 do
  830. begin
  831. faceGroup := mesh.FaceGroups[i] as TOBJFGVertexNormalTexIndexList;
  832. newMesh := TGLMeshObject.CreateOwned(Owner.MeshObjects);
  833. newMesh.Mode := momFaceGroups;
  834. newMesh.Name := faceGroup.Name;
  835. newfaceGroup := TOBJFGVertexNormalTexIndexList.CreateOwned(newMesh.FaceGroups);
  836. newfaceGroup.Assign(faceGroup);
  837. newfaceGroup.FName := faceGroup.Name;
  838. newfaceGroup.Mode := faceGroup.Mode;
  839. newfaceGroup.MaterialName := faceGroup.MaterialName;
  840. //SendInteger('VertexIndices', faceGroup.VertexIndices.Count);
  841. //SendInteger('TexCoords', faceGroup.TexCoordIndices.Count);
  842. //SendInteger('Normals', faceGroup.NormalIndices.Count);
  843. count := faceGroup.VertexIndices.Count;
  844. for j := 0 to count-1 do
  845. begin
  846. VertexIdx := faceGroup.VertexIndices[j];
  847. AffineVector := mesh.Vertices[VertexIdx];
  848. VertexIdx := newMesh.Vertices.Add(AffineVector);
  849. TexCoordIdx := faceGroup.TexCoordIndices[j];
  850. AffineVector := mesh.TexCoords[TexCoordIdx];
  851. TexCoordIdx := newMesh.TexCoords.Add(AffineVector);
  852. NormalIdx := faceGroup.NormalIndices[j];
  853. AffineVector := mesh.Normals[NormalIdx];
  854. NormalIdx := newMesh.Normals.Add(AffineVector);
  855. newfaceGroup.Add(VertexIdx, NormalIdx, TexCoordIdx);
  856. end;
  857. end;
  858. Owner.MeshObjects.RemoveAndFree(mesh);
  859. end;
  860. var
  861. command, objMtlFileName, curMtlName: string;
  862. {$IFDEF STATS}
  863. t0, t1, t2: Integer;
  864. {$ENDIF}
  865. begin
  866. {$IFDEF STATS}
  867. t0 := GetTickCount;
  868. {$ENDIF}
  869. FEof := False;
  870. FSourceStream := aStream;
  871. FLineNo := 0;
  872. objMtlFileName := '';
  873. curMtlName := '';
  874. mesh := TGLMeshObject.CreateOwned(Owner.MeshObjects);
  875. mesh.Mode := momFaceGroups;
  876. faceGroupNames := TStringList.Create;
  877. faceGroupNames.Duplicates := dupAccept;
  878. faceGroupNames.Sorted := True;
  879. try
  880. faceGroup := nil;
  881. while not FEof do
  882. begin
  883. ReadLine;
  884. if FLine = '' then
  885. Continue; { Skip blank line }
  886. if CharInSet(FLine[1], ['#', '$']) then
  887. Continue; { Skip comment and alternate comment }
  888. command := AnsiUpperCase(NextToken(FLine, ' '));
  889. if command = 'V' then
  890. begin
  891. ReadHomogeneousVector;
  892. Mesh.Vertices.Add(hv.X, hv.Y, hv.Z);
  893. end
  894. else if command = 'VT' then
  895. begin
  896. ReadAffineVector;
  897. Mesh.TexCoords.Add(av.X, av.Y, 0);
  898. end
  899. else if command = 'VN' then
  900. begin
  901. ReadAffineVector;
  902. Mesh.Normals.Add(av.X, av.Y, av.Z);
  903. end
  904. else if command = 'VP' then
  905. begin
  906. { Parameter Space Vertex: Ignore }
  907. end
  908. else if command = 'G' then
  909. begin
  910. { Only the first name on the line, multiple groups not supported. }
  911. SetCurrentFaceGroup(NextToken(FLine, ' '), curMtlName);
  912. end
  913. else if command = 'F' then
  914. begin
  915. ReadFace(curMtlName);
  916. end
  917. else if command = 'O' then
  918. begin
  919. { Object Name: Ignore }
  920. end
  921. else if command = 'MTLLIB' then
  922. begin
  923. objMtlFileName := NextToken(FLine, #13);
  924. end
  925. else if command = 'USEMTL' then
  926. begin
  927. curMtlName := GetOrAllocateMaterial(objMtlFileName, NextToken(FLine, ' '));
  928. if faceGroup = nil then
  929. SetCurrentFaceGroup('', curMtlName)
  930. else
  931. SetCurrentFaceGroup(faceGroup.FName, curMtlName);
  932. end
  933. else if command = 'S' then
  934. begin
  935. { Smooth Group: Ignore }
  936. end
  937. else if command = 'T' then
  938. begin
  939. ReadTriangleStrip;
  940. end
  941. else if command = 'Q' then
  942. begin
  943. ReadTriangleStripContinued;
  944. end
  945. else
  946. Error('Unsupported Command ''' + command + '''');
  947. end;
  948. mesh.FaceGroups.SortByMaterial;
  949. {$IFDEF STATS}
  950. t1 := GetTickCount;
  951. {$ENDIF}
  952. CalcMissingOBJNormals(mesh);
  953. {$IFDEF STATS}
  954. t2 := GetTickCount;
  955. InformationDlg(Format('TGLOBJVectorFile Loaded in %dms'#13 +
  956. #13 +
  957. ' %dms spent reading'#13 +
  958. ' %dms spent calculating normals'#13 +
  959. ' %d Geometric Vertices'#13 +
  960. ' %d Texture Vertices'#13 +
  961. ' %d Normals'#13 +
  962. ' %d FaceGroups/Strips',
  963. [t2 - t0,
  964. t1 - t0,
  965. t2 - t1,
  966. Mesh.Vertices.Count,
  967. Mesh.TexCoords.Count,
  968. Mesh.Normals.Count,
  969. Mesh.FaceGroups.Count]));
  970. {$ENDIF}
  971. if vGLFileOBJ_SplitMesh then
  972. SplitMesh;
  973. finally
  974. faceGroupNames.Free;
  975. end;
  976. end;
  977. procedure TGLOBJVectorFile.SaveToStream(aStream: TStream);
  978. var
  979. OldDecimalSeparator: char;
  980. procedure Write(const s: AnsiString);
  981. begin
  982. if s <> '' then
  983. aStream.Write(s[1], Length(s));
  984. end;
  985. procedure WriteLn(const s: string);
  986. begin
  987. Write(AnsiString(s));
  988. Write(#13#10);
  989. end;
  990. procedure WriteHeader;
  991. begin
  992. WriteLn('# OBJ-File exported by GLScene');
  993. WriteLn('');
  994. end;
  995. procedure WriteVertices;
  996. var
  997. s: string;
  998. j, i, n: integer;
  999. begin
  1000. n := 0;
  1001. for j := 0 to Owner.MeshObjects.Count - 1 do
  1002. begin
  1003. Writeln(Format('# Mesh %d', [j + 1]));
  1004. with Owner.MeshObjects[j].Vertices do
  1005. begin
  1006. for i := 0 to Count - 1 do
  1007. begin
  1008. s := Format('v %g %g %g', [List^[i].X,
  1009. List^[i].Y,
  1010. List^[i].Z]);
  1011. Writeln(s);
  1012. end;
  1013. Inc(n, Count);
  1014. end;
  1015. end;
  1016. WriteLn(Format('# %d Vertices', [n]));
  1017. WriteLn('');
  1018. end;
  1019. procedure WriteNormals;
  1020. var
  1021. s: string;
  1022. j, i, n: integer;
  1023. begin
  1024. n := 0;
  1025. for j := 0 to Owner.MeshObjects.Count - 1 do
  1026. begin
  1027. Writeln(Format('# Mesh %d', [j + 1]));
  1028. with Owner.MeshObjects[j].Normals do
  1029. begin
  1030. for i := 0 to Count - 1 do
  1031. begin
  1032. s := Format('vn %g %g %g', [List^[i].X,
  1033. List^[i].Y,
  1034. List^[i].Z]);
  1035. Writeln(s);
  1036. end;
  1037. Inc(n, Count);
  1038. end;
  1039. end;
  1040. WriteLn(Format('# %d Normals', [n]));
  1041. WriteLn('');
  1042. end;
  1043. procedure WriteTexCoords;
  1044. var
  1045. s: string;
  1046. j, i, n: integer;
  1047. begin
  1048. n := 0;
  1049. for j := 0 to Owner.MeshObjects.Count - 1 do
  1050. begin
  1051. Writeln(Format('# Mesh %d', [j + 1]));
  1052. with Owner.MeshObjects[j].TexCoords do
  1053. begin
  1054. for i := 0 to Count - 1 do
  1055. begin
  1056. s := Format('vt %g %g', [List^[i].X, List^[i].Y]);
  1057. Writeln(s);
  1058. end;
  1059. Inc(n, Count);
  1060. end;
  1061. end;
  1062. WriteLn(Format('# %d Texture-Coordinates', [n]));
  1063. WriteLn('');
  1064. end;
  1065. procedure WriteOBJFaceGroup(aFaceGroup: TOBJFGVertexNormalTexIndexList; o: Integer = 0);
  1066. var
  1067. vIdx, nIdx, tIdx: integer;
  1068. i, Index, Polygon: integer;
  1069. Line, t: string;
  1070. begin
  1071. with aFaceGroup do
  1072. begin
  1073. Index := 0;
  1074. for Polygon := 0 to PolygonVertices.Count - 1 do
  1075. begin
  1076. Line := 'f ';
  1077. for i := 1 to PolygonVertices[Polygon] do
  1078. begin
  1079. vIdx := VertexIndices[Index] + 1 + o;
  1080. nIdx := NormalIndices[Index] + 1 + o;
  1081. tIdx := TexCoordIndices[Index] + 1 + o;
  1082. t := IntToStr(vIdx) + '/';
  1083. if tIdx = -1 then
  1084. t := t + '/'
  1085. else
  1086. t := t + IntToStr(tIdx) + '/';
  1087. if nIdx = -1 then
  1088. t := t + '/'
  1089. else
  1090. t := t + IntToStr(nIdx) + '/';
  1091. Line := Line + Copy(t, 1, length(t) - 1) + ' ';
  1092. inc(Index);
  1093. end;
  1094. Writeln(Line);
  1095. end;
  1096. end;
  1097. Writeln('');
  1098. end;
  1099. procedure WriteVertexIndexList(fg: TFGVertexIndexList; o: Integer = 0);
  1100. var
  1101. i, n: Integer;
  1102. begin
  1103. case fg.Mode of
  1104. fgmmTriangles:
  1105. begin
  1106. n := fg.VertexIndices.Count - 3;
  1107. i := 0;
  1108. while i <= n do
  1109. begin
  1110. Writeln(Format('f %d/%0:d %d/%1:d %d/%2:d',
  1111. [fg.VertexIndices[i] + 1 + o,
  1112. fg.VertexIndices[i + 1] + 1 + o,
  1113. fg.VertexIndices[i + 2] + 1 + o]));
  1114. Inc(i, 3);
  1115. end;
  1116. end;
  1117. fgmmTriangleFan:
  1118. begin
  1119. Write('f ');
  1120. n := fg.VertexIndices.Count - 1;
  1121. i := 0;
  1122. while i <= n do
  1123. begin
  1124. if i < n then
  1125. Write(AnsiString(Format('%d/%0:d ', [fg.VertexIndices[i] + 1 + o])))
  1126. else
  1127. Writeln(Format('%d/%0:d', [fg.VertexIndices[i] + 1 + o]));
  1128. Inc(i);
  1129. end;
  1130. end;
  1131. fgmmTriangleStrip:
  1132. begin
  1133. n := fg.VertexIndices.Count - 3;
  1134. i := 0;
  1135. while i <= n do
  1136. begin
  1137. Writeln(Format('f %d/%0:d %d/%1:d %d/%2:d',
  1138. [fg.VertexIndices[i] + 1 + o,
  1139. fg.VertexIndices[i + 1] + 1 + o,
  1140. fg.VertexIndices[i + 2] + 1 + o]));
  1141. Inc(i);
  1142. end;
  1143. end;
  1144. end;
  1145. end;
  1146. procedure WriteFaceGroups;
  1147. var
  1148. j, i, k: Integer;
  1149. fg: TGLFaceGroup;
  1150. MoName: string;
  1151. begin
  1152. k := 0;
  1153. for j := 0 to Owner.MeshObjects.Count - 1 do
  1154. begin
  1155. MoName := Owner.MeshObjects[j].Name;
  1156. if MoName = '' then
  1157. MoName := Format('Mesh%d', [j + 1]);
  1158. Writeln('g ' + MoName);
  1159. for i := 0 to Owner.MeshObjects[j].FaceGroups.Count - 1 do
  1160. begin
  1161. Writeln(Format('s %d', [i + 1]));
  1162. fg := Owner.MeshObjects[j].FaceGroups[i];
  1163. if fg is TOBJFGVertexNormalTexIndexList then
  1164. WriteOBJFaceGroup(TOBJFGVertexNormalTexIndexList(fg), k)
  1165. else if fg is TFGVertexIndexList then
  1166. WriteVertexIndexList(TFGVertexIndexList(fg), k)
  1167. else
  1168. Assert(False); //unsupported face group
  1169. end;
  1170. //advance vertex index offset
  1171. Inc(k, Owner.MeshObjects[j].Vertices.Count);
  1172. end;
  1173. end;
  1174. begin
  1175. Assert(Owner is TGLFreeForm, 'Can only save FreeForms.');
  1176. OldDecimalSeparator := FormatSettings.DecimalSeparator;
  1177. FormatSettings.DecimalSeparator := '.';
  1178. { Better not call anything that wants the system-locale intact
  1179. from this block }
  1180. try
  1181. WriteHeader;
  1182. WriteVertices;
  1183. WriteNormals;
  1184. WriteTexCoords;
  1185. WriteFaceGroups;
  1186. finally
  1187. FormatSettings.DecimalSeparator := OldDecimalSeparator;
  1188. end;
  1189. end;
  1190. // ------------------
  1191. // ------------------ TGLMTLFile ------------------
  1192. // ------------------
  1193. procedure TGLMTLFile.Prepare;
  1194. var
  1195. i: Integer;
  1196. buf: string;
  1197. begin
  1198. // "standardize" the mtl file lines
  1199. for i := Count - 1 downto 0 do
  1200. begin
  1201. buf := UpperCase(Trim(Strings[i]));
  1202. if (buf = '') or CharInSet(buf[1], ['#', '$']) then
  1203. Delete(i)
  1204. else
  1205. begin
  1206. Strings[i] := StringReplace(buf, #9, #32, [rfIgnoreCase]);
  1207. end;
  1208. end;
  1209. end;
  1210. function TGLMTLFile.MaterialStringProperty(const materialName, propertyName: string): string;
  1211. var
  1212. i, n: Integer;
  1213. buf, line: string;
  1214. begin
  1215. buf := 'NEWMTL ' + UpperCase(materialName);
  1216. i := IndexOf(buf);
  1217. if i >= 0 then
  1218. begin
  1219. buf := UpperCase(propertyName) + ' ';
  1220. n := Length(buf);
  1221. Inc(i);
  1222. while i < Count do
  1223. begin
  1224. line := Strings[i];
  1225. if Copy(line, 1, 7) = 'NEWMTL ' then
  1226. Break;
  1227. if Copy(line, 1, n) = buf then
  1228. begin
  1229. Result := Copy(Strings[i], n + 1, MaxInt);
  1230. Exit;
  1231. end;
  1232. Inc(i);
  1233. end;
  1234. end;
  1235. Result := '';
  1236. end;
  1237. function TGLMTLFile.MaterialVectorProperty(const materialName, propertyName: string;
  1238. const defaultValue: TGLVector): TGLVector;
  1239. var
  1240. i: Integer;
  1241. sl: TStringList;
  1242. begin
  1243. sl := TStringList.Create;
  1244. try
  1245. sl.CommaText := MaterialStringProperty(materialName, propertyName);
  1246. if sl.Count > 0 then
  1247. begin
  1248. Result := NullHmgVector;
  1249. for i := 0 to 3 do
  1250. if sl.Count > i then
  1251. Result.V[i] := GLStrToFloatDef(sl[i], 0)
  1252. else
  1253. Break;
  1254. end
  1255. else
  1256. Result := defaultValue;
  1257. finally
  1258. sl.Free;
  1259. end;
  1260. end;
  1261. procedure TOBJFGVertexNormalTexIndexList.Assign(Source: TPersistent);
  1262. begin
  1263. if Source is TOBJFGVertexNormalTexIndexList then
  1264. begin
  1265. FMode := TOBJFGVertexNormalTexIndexList(Source).FMode;
  1266. FName := TOBJFGVertexNormalTexIndexList(Source).FName;
  1267. FCurrentVertexCount := TOBJFGVertexNormalTexIndexList(Source).FCurrentVertexCount;
  1268. FShowNormals := TOBJFGVertexNormalTexIndexList(Source).FShowNormals;
  1269. if TOBJFGVertexNormalTexIndexList(Source).FPolygonVertices = nil then
  1270. FreeAndNil(FPolygonVertices)
  1271. else
  1272. begin
  1273. if FPolygonVertices = nil then
  1274. FPolygonVertices := TGLIntegerList.Create;
  1275. FPolygonVertices.Assign(TOBJFGVertexNormalTexIndexList(Source).FPolygonVertices);
  1276. end;
  1277. end
  1278. else
  1279. inherited;
  1280. end;
  1281. procedure TOBJFGVertexNormalTexIndexList.ReadFromFiler(
  1282. reader: TGLVirtualReader);
  1283. var
  1284. archiveVersion: Integer;
  1285. begin
  1286. inherited ReadFromFiler(reader);
  1287. archiveVersion := reader.ReadInteger;
  1288. if archiveVersion = 0 then
  1289. begin
  1290. FMode := TOBJFGMode(reader.ReadInteger);
  1291. FName := reader.ReadString;
  1292. FCurrentVertexCount := reader.ReadInteger;
  1293. FShowNormals := reader.ReadBoolean;
  1294. if FMode = objfgmmPolygons then
  1295. begin
  1296. FPolygonVertices := TGLIntegerList.Create;
  1297. FPolygonVertices.ReadFromFiler(reader);
  1298. end;
  1299. end
  1300. else
  1301. RaiseFilerException(archiveVersion);
  1302. end;
  1303. procedure TOBJFGVertexNormalTexIndexList.WriteToFiler(
  1304. writer: TGLVirtualWriter);
  1305. begin
  1306. inherited WriteToFiler(writer);
  1307. with writer do
  1308. begin
  1309. WriteInteger(0); // Archive Version 0
  1310. writer.WriteInteger(Ord(FMode));
  1311. writer.WriteString(FName);
  1312. writer.WriteInteger(FCurrentVertexCount);
  1313. writer.WriteBoolean(FShowNormals);
  1314. if FPolygonVertices <> nil then
  1315. FPolygonVertices.WriteToFiler(writer);
  1316. end;
  1317. end;
  1318. //-------------------------------------------------
  1319. initialization
  1320. //-------------------------------------------------
  1321. RegisterVectorFileFormat('obj', 'WaveFront model file', TGLOBJVectorFile);
  1322. RegisterVectorFileFormat('objf', 'Stripe model file', TGLOBJVectorFile);
  1323. RegisterClass(TOBJFGVertexNormalTexIndexList);
  1324. end.