GLFileOBJ.pas 41 KB

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