GLS.FileOBJ.pas 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.FileOBJ;
  5. (*
  6. Support-Code to load Wavefront OBJ Files into TGLFreeForm-Components
  7. in GLScene.
  8. Note that you must manually add this unit to one of your project's uses
  9. to enable support for OBJ & OBJF at run-time.
  10. *)
  11. interface
  12. {$I GLScene.inc}
  13. {.$DEFINE STATS} // Define to display statistics after loading.
  14. uses
  15. Winapi.OpenGL,
  16. Winapi.OpenGLext,
  17. System.Classes,
  18. System.SysUtils,
  19. GLS.VectorTypes,
  20. GLS.ApplicationFileIO,
  21. GLS.PersistentClasses,
  22. GLS.VectorGeometry,
  23. GLS.Scene,
  24. GLS.VectorFileObjects,
  25. GLS.VectorLists,
  26. GLS.Texture,
  27. GLS.Color,
  28. GLS.RenderContextInfo,
  29. GLS.Material,
  30. GLS.Utils;
  31. const
  32. // Load input data in chunks of BufSize Bytes.
  33. BufSize = 10240;
  34. // Allocate memory for the current line in chunks of LineLen Bytes.
  35. LineLen = 100;
  36. type
  37. TGLOBJVectorFile = class(TGLVectorFile)
  38. private
  39. FSourceStream: TStream; // Load from this stream
  40. FBuffer: AnsiString; // Buffer
  41. FLine: string; // current line
  42. FLineNo: Integer; // current Line number - for error messages
  43. FEof: Boolean; // Stream done?
  44. FBufPos: Integer; // Position in the buffer
  45. protected
  46. // Read a single line of text from the source stream, set FEof to true when done.
  47. procedure ReadLine;
  48. // Raise a class-specific exception
  49. procedure Error(const msg: string);
  50. procedure CalcMissingOBJNormals(mesh: TGLMeshObject);
  51. public
  52. class function Capabilities: TGLDataFileCapabilities; override;
  53. procedure LoadFromStream(aStream: TStream); override;
  54. procedure SaveToStream(aStream: TStream); override;
  55. end;
  56. EGLOBJFileError = class(Exception)
  57. private
  58. FLineNo: Integer;
  59. public
  60. property LineNo: Integer read FLineNo;
  61. end;
  62. (*A simple class that know how to extract infos from a mtl file.
  63. mtl files are companion files of the obj, they store material
  64. information. Guessed content (imported ones denoted with a '*',
  65. please help if you know more):
  66. materials begin with a 'newmtl' command followed by material name
  67. *Kd command defines the diffuse color
  68. *map_Kd command defines the diffuse texture image
  69. *Ka/map_Ka define the ambient color and texmap
  70. *Ks/map_Ks define the specular color and texmap
  71. *Ke/map_Ke define the self-illumination/lightmap texmap
  72. map_Bump specifies the bump map
  73. *d specifies transparency (alpha-channel, range [0; 1])
  74. map_d specifies the opcaity texture map
  75. Ns defines the specular exponent or shininess or phong specular (?)
  76. Ni is the refraction index (greater than 1)
  77. *illum defines the illumination model (0 for no lighting, 1 for
  78. ambient and diffuse, 2 for full lighting) *)
  79. TGLMTLFile = class(TStringList)
  80. public
  81. procedure Prepare;
  82. function MaterialStringProperty(const materialName, propertyName: string): string;
  83. function MaterialVectorProperty(const materialName, propertyName: string;
  84. const defaultValue: TGLVector): TGLVector;
  85. end;
  86. var
  87. (* If enabled, main mesh will be splitted into multiple mesh from facegroup data.*)
  88. vGLFileOBJ_SplitMesh: boolean = False;
  89. // ------------------------------------------------------------------
  90. implementation
  91. // ------------------------------------------------------------------
  92. uses
  93. GLS.Strings,
  94. GLS.OpenGLTokens,
  95. GLS.XOpenGL,
  96. GLS.Context,
  97. GLS.MeshUtils;
  98. function StreamEOF(S: TStream): Boolean;
  99. begin
  100. // Is the stream at its end?
  101. Result := (S.Position >= S.Size);
  102. end;
  103. function Rest(const s: string; Count: integer): string;
  104. // Return the right part of s including s[Count].
  105. begin
  106. Result := copy(s, Count, Length(s) - Count + 1);
  107. end;
  108. // Return the next Delimiter-delimited Token from the string s and remove it from s
  109. function NextToken(var s: string; delimiter: Char): string;
  110. var
  111. p: Integer;
  112. begin
  113. p := Pos(Delimiter, s);
  114. if p = 0 then
  115. begin
  116. Result := s;
  117. s := '';
  118. end
  119. else
  120. begin
  121. Result := copy(s, 1, p - 1);
  122. s := TrimLeft(Rest(s, p + 1));
  123. end;
  124. end;
  125. (* ** TOBJFGVertexNormalTexIndexList ******************************************
  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: TGLIntegerList;
  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: TGLVirtualWriter); override;
  151. procedure ReadFromFiler(reader: TGLVirtualReader); override;
  152. procedure Add(VertexIdx, NormalIdx, TexCoordIdx: Integer);
  153. procedure BuildList(var mrci: TGLRenderContextInfo); override;
  154. procedure AddToTriangles(aList: TGLAffineVectorList;
  155. aTexCoords: TGLAffineVectorList = nil;
  156. aNormals: TGLAffineVectorList = 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: TGLIntegerList 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 := TGLIntegerList.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: TGLAffineVectorList;
  326. aTexCoords: TGLAffineVectorList = nil;
  327. aNormals: TGLAffineVectorList = nil);
  328. var
  329. i, j, n, n0: Integer;
  330. vertexList, texCoordList, normalsList: TGLAffineVectorList;
  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: TGLMeshObject);
  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 are missing
  520. Index := 0; // Current index into the Index-List of this Facegroup.
  521. // For every Polygon
  522. for Polygon := 0 to FPolygonVertices.Count - 1 do
  523. begin
  524. // Init
  525. FirstVertexIndex := Index;
  526. FillChar(p, SizeOf(p), 0);
  527. // Last Vertex in this polygon
  528. p[2] := @VertexPool^[VertexIndices.List^[Index + FPolygonVertices[Polygon] - 1]];
  529. // First Vertex in this polygon
  530. p[3] := @VertexPool^[VertexIndices.List^[Index]];
  531. // For every Vertex in the current Polygon but the last.
  532. for j := 0 to FPolygonVertices[Polygon] - 2 do
  533. begin
  534. Move(p[2], p[1], 2 * SizeOf(PAffineVector));
  535. p[3] := @VertexPool^[VertexIndices.List^[Index + 1]];
  536. DoCalcNormal;
  537. Inc(Index);
  538. end;
  539. // For the last vertex use the first as partner to span the plane.
  540. Move(p[2], p[1], 2 * SizeOf(PAffineVector));
  541. p[3] := @VertexPool^[VertexIndices.List^[FirstVertexIndex]];
  542. DoCalcNormal;
  543. inc(Index);
  544. end; // of for FPolygonVertices
  545. end; // of with Facegroup
  546. end;
  547. procedure CalcForTriangleStrip;
  548. begin
  549. end;
  550. begin
  551. // Shorthand notations.
  552. VertexPool := Mesh.Vertices.List;
  553. for Face := 0 to Mesh.FaceGroups.Count - 1 do
  554. begin
  555. FG := TOBJFGVertexNormalTexIndexList(Mesh.FaceGroups[Face]);
  556. case FG.Mode of
  557. objfgmmPolygons: CalcForPolygons;
  558. objfgmmTriangleStrip: CalcForTriangleStrip;
  559. end;
  560. end;
  561. end;
  562. procedure TGLOBJVectorFile.LoadFromStream(aStream: TStream);
  563. var
  564. hv: THomogeneousVector;
  565. av: TAffineVector;
  566. mesh: TGLMeshObject;
  567. faceGroup: TOBJFGVertexNormalTexIndexList;
  568. faceGroupNames: TStringList;
  569. // Read a vector with a maximum of 4 elements from the current line.
  570. procedure ReadHomogeneousVector;
  571. var
  572. i, c: Integer;
  573. f: string;
  574. begin
  575. FillChar(hv, SizeOf(hv), 0);
  576. i := 0;
  577. while (FLine <> '') and (i < 4) do
  578. begin
  579. f := NextToken(FLine, ' ');
  580. Val(f, hv.V[i], c);
  581. if c <> 0 then
  582. Error(Format('''%s'' is not a valid floating-point constant.', [f]));
  583. Inc(i);
  584. end;
  585. end;
  586. // Read a vector with a maximum of 3 elements from the current line.
  587. procedure ReadAffineVector;
  588. var
  589. i, c: integer;
  590. f: string;
  591. begin
  592. FillChar(av, SizeOf(av), 0);
  593. i := 0;
  594. while (FLine <> '') and (i < 3) do
  595. begin
  596. f := NextToken(FLine, ' ');
  597. Val(f, av.V[i], c);
  598. if c <> 0 then
  599. Error(Format('''%s'' is not a valid floating-point constant.', [f]));
  600. inc(i);
  601. end;
  602. end;
  603. procedure SetCurrentFaceGroup(aName: string; const matlName: string);
  604. var
  605. i: Integer;
  606. begin
  607. i := faceGroupNames.IndexOf(aName);
  608. if i >= 0 then
  609. begin
  610. faceGroup := TOBJFGVertexNormalTexIndexList(faceGroupNames.Objects[i]);
  611. if faceGroup.MaterialName <> matlName then
  612. begin
  613. i := faceGroupNames.IndexOf(aName);
  614. if i >= 0 then
  615. begin
  616. faceGroup := TOBJFGVertexNormalTexIndexList(faceGroupNames.Objects[i]);
  617. if faceGroup.MaterialName <> matlName then
  618. faceGroup := nil;
  619. end;
  620. end;
  621. end;
  622. if (faceGroup = nil) or (faceGroup.Name <> aName)
  623. or (faceGroup.PolygonVertices.Count > 0)
  624. or (faceGroup.MaterialName <> matlName) then
  625. begin
  626. faceGroup := TOBJFGVertexNormalTexIndexList.CreateOwned(Mesh.FaceGroups);
  627. faceGroup.FName := aName;
  628. faceGroup.Mode := objfgmmPolygons;
  629. faceGroup.MaterialName := matlName;
  630. faceGroupNames.AddObject(aName, faceGroup);
  631. end;
  632. end;
  633. procedure AddFaceVertex(faceVertices: string);
  634. function GetIndex(Count: Integer): Integer;
  635. var
  636. s: string;
  637. begin
  638. s := NextToken(FaceVertices, '/');
  639. Result := StrToIntDef(s, 0);
  640. if Result = 0 then
  641. Result := -1 // Missing
  642. else if Result < 0 then
  643. begin
  644. // Relative, make absolute. "-1" means last, "-2" second last.
  645. Result := Count + Result
  646. end
  647. else
  648. begin
  649. // Absolute, correct for zero-base.
  650. Dec(Result);
  651. end;
  652. end;
  653. var
  654. vIdx, tIdx, nIdx: Integer;
  655. begin
  656. vIdx := GetIndex(mesh.Vertices.Count);
  657. tIdx := GetIndex(mesh.TexCoords.Count);
  658. nIdx := GetIndex(mesh.Normals.Count);
  659. faceGroup.Add(vIdx, nIdx, tIdx);
  660. end;
  661. procedure ReadFace(const curMtlName: string);
  662. var
  663. faceVertices: string;
  664. begin
  665. if FLine <> '' then
  666. begin
  667. if not Assigned(FaceGroup) then
  668. SetCurrentFaceGroup('', curMtlName);
  669. try
  670. while FLine <> '' do
  671. begin
  672. faceVertices := NextToken(FLine, ' ');
  673. AddFaceVertex(faceVertices);
  674. end;
  675. finally
  676. FaceGroup.PolygonComplete;
  677. end;
  678. end;
  679. end;
  680. procedure ReadTriangleStripContinued;
  681. var
  682. faceVertices: string;
  683. begin
  684. if faceGroup = nil then
  685. Error('q-line without preceding t-line.');
  686. while FLine <> '' do
  687. begin
  688. FaceVertices := NextToken(FLine, ' ');
  689. AddFaceVertex(FaceVertices);
  690. end;
  691. end;
  692. procedure ReadTriangleStrip;
  693. begin
  694. // Start a new Facegroup, mode=triangle strip
  695. faceGroup := TOBJFGVertexNormalTexIndexList.CreateOwned(Mesh.FaceGroups);
  696. faceGroup.Mode := objfgmmTriangleStrip;
  697. // The rest is the same as for continuation of a strip.
  698. ReadTriangleStripContinued;
  699. end;
  700. function GetOrAllocateMaterial(const libName, matName: string): string;
  701. var
  702. fs: TStream;
  703. objMtl: TGLMTLFile;
  704. matLib: TGLMaterialLibrary;
  705. libMat, libMat2: TGLLibMaterial;
  706. texName: string;
  707. libFilename: string;
  708. begin
  709. if GetOwner is TGLBaseMesh then
  710. begin
  711. // got a linked material library?
  712. matLib := TGLBaseMesh(GetOwner).MaterialLibrary;
  713. if Assigned(matLib) then
  714. begin
  715. Result := matName;
  716. libMat := matLib.Materials.GetLibMaterialByName(matName);
  717. if not Assigned(libMat) then
  718. begin
  719. // spawn a new material
  720. libMat := matLib.Materials.Add;
  721. libMat.Name := matName;
  722. // get full path for material file to be load
  723. if matLib.TexturePaths = EmptyStr then
  724. libFilename := libName
  725. else
  726. libFilename := IncludeTrailingPathDelimiter(matLib.TexturePaths) + libName;
  727. try
  728. fs := TFileStream.Create(libFilename,fmOpenRead);
  729. except
  730. fs := nil;
  731. end;
  732. if Assigned(fs) then
  733. begin
  734. objMtl := TGLMTLFile.Create;
  735. try
  736. objMtl.LoadFromStream(fs);
  737. objMtl.Prepare;
  738. // setup material colors
  739. with libMat.Material.FrontProperties do
  740. begin
  741. Ambient.Color := objMtl.MaterialVectorProperty(matName, 'Ka', clrGray20);
  742. Diffuse.Color := objMtl.MaterialVectorProperty(matName, 'Kd', clrGray80);
  743. Diffuse.Alpha := GLStrToFloatDef(objMtl.MaterialStringProperty(matName, 'd'), 1);
  744. if Diffuse.Alpha < 1 then
  745. libMat.Material.BlendingMode := bmTransparency;
  746. case StrToIntDef(objMtl.MaterialStringProperty(matName, 'illum'), 1) of
  747. 0:
  748. begin // non-lit material
  749. libMat.Material.MaterialOptions := [moNoLighting];
  750. end;
  751. 1: ; // flat, non-shiny material
  752. 2:
  753. begin // specular material
  754. Specular.Color := objMtl.MaterialVectorProperty(matName, 'Ks', clrTransparent);
  755. end;
  756. else
  757. // unknown, assume unlit
  758. libMat.Material.MaterialOptions := [moNoLighting];
  759. Diffuse.Color := clrGray80;
  760. end;
  761. Shininess := StrToIntDef(objMtl.MaterialStringProperty(matName, 'Ns'), 1);
  762. end;
  763. // setup texture
  764. texName := objMtl.MaterialStringProperty(matName, 'map_Kd');
  765. if texName <> '' then
  766. begin
  767. try
  768. with libMat.Material.Texture do
  769. begin
  770. Image.LoadFromFile(texName);
  771. Disabled := False;
  772. TextureMode := tmModulate;
  773. end;
  774. except
  775. on E: ETexture do
  776. begin
  777. if not Owner.IgnoreMissingTextures then
  778. raise;
  779. end;
  780. end;
  781. end;
  782. // setup lightmap (self-illumination) texture
  783. texName := objMtl.MaterialStringProperty(matName, 'map_Ke');
  784. if texName <> '' then
  785. begin
  786. // spawn a new material
  787. libMat2 := matLib.Materials.Add;
  788. libMat2.Name := matName + '_lm';
  789. // Use the GLScene built-in second texture support (note: the mesh LightmapProperty MUST be empty)
  790. libMat.Texture2Name := libMat2.Name;
  791. try
  792. with libMat2.Material.Texture do
  793. begin
  794. Image.LoadFromFile(texName);
  795. Disabled := False;
  796. minFilter := miLinear;
  797. TextureWrap := twNone;
  798. TextureFormat := tfRGB;
  799. TextureMode := tmModulate;
  800. end;
  801. except
  802. on E: ETexture do
  803. begin
  804. if not Owner.IgnoreMissingTextures then
  805. raise;
  806. end;
  807. end;
  808. end;
  809. finally
  810. objMtl.Free;
  811. fs.Free;
  812. end;
  813. end;
  814. end
  815. else
  816. Result := matName;
  817. end
  818. else
  819. Result := '';
  820. end;
  821. end;
  822. procedure SplitMesh;
  823. var
  824. i, j, count: Integer;
  825. newMesh: TGLMeshObject;
  826. newfaceGroup: TOBJFGVertexNormalTexIndexList;
  827. VertexIdx, NormalIdx, TexCoordIdx: Integer;
  828. AffineVector: TAffineVector;
  829. begin
  830. for i := 0 to mesh.FaceGroups.Count-1 do
  831. begin
  832. faceGroup := mesh.FaceGroups[i] as TOBJFGVertexNormalTexIndexList;
  833. newMesh := TGLMeshObject.CreateOwned(Owner.MeshObjects);
  834. newMesh.Mode := momFaceGroups;
  835. newMesh.Name := faceGroup.Name;
  836. newfaceGroup := TOBJFGVertexNormalTexIndexList.CreateOwned(newMesh.FaceGroups);
  837. newfaceGroup.Assign(faceGroup);
  838. newfaceGroup.FName := faceGroup.Name;
  839. newfaceGroup.Mode := faceGroup.Mode;
  840. newfaceGroup.MaterialName := faceGroup.MaterialName;
  841. //SendInteger('VertexIndices', faceGroup.VertexIndices.Count);
  842. //SendInteger('TexCoords', faceGroup.TexCoordIndices.Count);
  843. //SendInteger('Normals', faceGroup.NormalIndices.Count);
  844. count := faceGroup.VertexIndices.Count;
  845. for j := 0 to count-1 do
  846. begin
  847. VertexIdx := faceGroup.VertexIndices[j];
  848. AffineVector := mesh.Vertices[VertexIdx];
  849. VertexIdx := newMesh.Vertices.Add(AffineVector);
  850. TexCoordIdx := faceGroup.TexCoordIndices[j];
  851. AffineVector := mesh.TexCoords[TexCoordIdx];
  852. TexCoordIdx := newMesh.TexCoords.Add(AffineVector);
  853. NormalIdx := faceGroup.NormalIndices[j];
  854. AffineVector := mesh.Normals[NormalIdx];
  855. NormalIdx := newMesh.Normals.Add(AffineVector);
  856. newfaceGroup.Add(VertexIdx, NormalIdx, TexCoordIdx);
  857. end;
  858. end;
  859. Owner.MeshObjects.RemoveAndFree(mesh);
  860. end;
  861. var
  862. command, objMtlFileName, curMtlName: string;
  863. {$IFDEF STATS}
  864. t0, t1, t2: Integer;
  865. {$ENDIF}
  866. begin
  867. {$IFDEF STATS}
  868. t0 := GetTickCount;
  869. {$ENDIF}
  870. FEof := False;
  871. FSourceStream := aStream;
  872. FLineNo := 0;
  873. objMtlFileName := '';
  874. curMtlName := '';
  875. mesh := TGLMeshObject.CreateOwned(Owner.MeshObjects);
  876. mesh.Mode := momFaceGroups;
  877. faceGroupNames := TStringList.Create;
  878. faceGroupNames.Duplicates := dupAccept;
  879. faceGroupNames.Sorted := True;
  880. try
  881. faceGroup := nil;
  882. while not FEof do
  883. begin
  884. ReadLine;
  885. if FLine = '' then
  886. Continue; { Skip blank line }
  887. if CharInSet(FLine[1], ['#', '$']) then
  888. Continue; { Skip comment and alternate comment }
  889. command := AnsiUpperCase(NextToken(FLine, ' '));
  890. if command = 'V' then
  891. begin
  892. ReadHomogeneousVector;
  893. Mesh.Vertices.Add(hv.X, hv.Y, hv.Z);
  894. end
  895. else if command = 'VT' then
  896. begin
  897. ReadAffineVector;
  898. Mesh.TexCoords.Add(av.X, av.Y, 0);
  899. end
  900. else if command = 'VN' then
  901. begin
  902. ReadAffineVector;
  903. Mesh.Normals.Add(av.X, av.Y, av.Z);
  904. end
  905. else if command = 'VP' then
  906. begin
  907. { Parameter Space Vertex: Ignore }
  908. end
  909. else if command = 'G' then
  910. begin
  911. { Only the first name on the line, multiple groups not supported. }
  912. SetCurrentFaceGroup(NextToken(FLine, ' '), curMtlName);
  913. end
  914. else if command = 'F' then
  915. begin
  916. ReadFace(curMtlName);
  917. end
  918. else if command = 'O' then
  919. begin
  920. { Object Name: Ignore }
  921. end
  922. else if command = 'MTLLIB' then
  923. begin
  924. objMtlFileName := NextToken(FLine, #13);
  925. end
  926. else if command = 'USEMTL' then
  927. begin
  928. curMtlName := GetOrAllocateMaterial(objMtlFileName, NextToken(FLine, ' '));
  929. if faceGroup = nil then
  930. SetCurrentFaceGroup('', curMtlName)
  931. else
  932. SetCurrentFaceGroup(faceGroup.FName, curMtlName);
  933. end
  934. else if command = 'S' then
  935. begin
  936. { Smooth Group: Ignore }
  937. end
  938. else if command = 'T' then
  939. begin
  940. ReadTriangleStrip;
  941. end
  942. else if command = 'Q' then
  943. begin
  944. ReadTriangleStripContinued;
  945. end
  946. else
  947. Error('Unsupported Command ''' + command + '''');
  948. end;
  949. mesh.FaceGroups.SortByMaterial;
  950. {$IFDEF STATS}
  951. t1 := GetTickCount;
  952. {$ENDIF}
  953. CalcMissingOBJNormals(mesh);
  954. {$IFDEF STATS}
  955. t2 := GetTickCount;
  956. InformationDlg(Format('TGLOBJVectorFile Loaded in %dms'#13 +
  957. #13 +
  958. ' %dms spent reading'#13 +
  959. ' %dms spent calculating normals'#13 +
  960. ' %d Geometric Vertices'#13 +
  961. ' %d Texture Vertices'#13 +
  962. ' %d Normals'#13 +
  963. ' %d FaceGroups/Strips',
  964. [t2 - t0,
  965. t1 - t0,
  966. t2 - t1,
  967. Mesh.Vertices.Count,
  968. Mesh.TexCoords.Count,
  969. Mesh.Normals.Count,
  970. Mesh.FaceGroups.Count]));
  971. {$ENDIF}
  972. if vGLFileOBJ_SplitMesh then
  973. SplitMesh;
  974. finally
  975. faceGroupNames.Free;
  976. end;
  977. end;
  978. procedure TGLOBJVectorFile.SaveToStream(aStream: TStream);
  979. var
  980. OldDecimalSeparator: char;
  981. procedure Write(const s: AnsiString);
  982. begin
  983. if s <> '' then
  984. aStream.Write(s[1], Length(s));
  985. end;
  986. procedure WriteLn(const s: string);
  987. begin
  988. Write(AnsiString(s));
  989. Write(#13#10);
  990. end;
  991. procedure WriteHeader;
  992. begin
  993. WriteLn('# OBJ-File exported by GLScene');
  994. WriteLn('');
  995. end;
  996. procedure WriteVertices;
  997. var
  998. s: string;
  999. j, i, n: integer;
  1000. begin
  1001. n := 0;
  1002. for j := 0 to Owner.MeshObjects.Count - 1 do
  1003. begin
  1004. Writeln(Format('# Mesh %d', [j + 1]));
  1005. with Owner.MeshObjects[j].Vertices do
  1006. begin
  1007. for i := 0 to Count - 1 do
  1008. begin
  1009. s := Format('v %g %g %g', [List^[i].X,
  1010. List^[i].Y,
  1011. List^[i].Z]);
  1012. Writeln(s);
  1013. end;
  1014. Inc(n, Count);
  1015. end;
  1016. end;
  1017. WriteLn(Format('# %d Vertices', [n]));
  1018. WriteLn('');
  1019. end;
  1020. procedure WriteNormals;
  1021. var
  1022. s: string;
  1023. j, i, n: integer;
  1024. begin
  1025. n := 0;
  1026. for j := 0 to Owner.MeshObjects.Count - 1 do
  1027. begin
  1028. Writeln(Format('# Mesh %d', [j + 1]));
  1029. with Owner.MeshObjects[j].Normals do
  1030. begin
  1031. for i := 0 to Count - 1 do
  1032. begin
  1033. s := Format('vn %g %g %g', [List^[i].X,
  1034. List^[i].Y,
  1035. List^[i].Z]);
  1036. Writeln(s);
  1037. end;
  1038. Inc(n, Count);
  1039. end;
  1040. end;
  1041. WriteLn(Format('# %d Normals', [n]));
  1042. WriteLn('');
  1043. end;
  1044. procedure WriteTexCoords;
  1045. var
  1046. s: string;
  1047. j, i, n: integer;
  1048. begin
  1049. n := 0;
  1050. for j := 0 to Owner.MeshObjects.Count - 1 do
  1051. begin
  1052. Writeln(Format('# Mesh %d', [j + 1]));
  1053. with Owner.MeshObjects[j].TexCoords do
  1054. begin
  1055. for i := 0 to Count - 1 do
  1056. begin
  1057. s := Format('vt %g %g', [List^[i].X, List^[i].Y]);
  1058. Writeln(s);
  1059. end;
  1060. Inc(n, Count);
  1061. end;
  1062. end;
  1063. WriteLn(Format('# %d Texture-Coordinates', [n]));
  1064. WriteLn('');
  1065. end;
  1066. procedure WriteOBJFaceGroup(aFaceGroup: TOBJFGVertexNormalTexIndexList; o: Integer = 0);
  1067. var
  1068. vIdx, nIdx, tIdx: integer;
  1069. i, Index, Polygon: integer;
  1070. Line, t: string;
  1071. begin
  1072. with aFaceGroup do
  1073. begin
  1074. Index := 0;
  1075. for Polygon := 0 to PolygonVertices.Count - 1 do
  1076. begin
  1077. Line := 'f ';
  1078. for i := 1 to PolygonVertices[Polygon] do
  1079. begin
  1080. vIdx := VertexIndices[Index] + 1 + o;
  1081. nIdx := NormalIndices[Index] + 1 + o;
  1082. tIdx := TexCoordIndices[Index] + 1 + o;
  1083. t := IntToStr(vIdx) + '/';
  1084. if tIdx = -1 then
  1085. t := t + '/'
  1086. else
  1087. t := t + IntToStr(tIdx) + '/';
  1088. if nIdx = -1 then
  1089. t := t + '/'
  1090. else
  1091. t := t + IntToStr(nIdx) + '/';
  1092. Line := Line + Copy(t, 1, length(t) - 1) + ' ';
  1093. inc(Index);
  1094. end;
  1095. Writeln(Line);
  1096. end;
  1097. end;
  1098. Writeln('');
  1099. end;
  1100. procedure WriteVertexIndexList(fg: TFGVertexIndexList; o: Integer = 0);
  1101. var
  1102. i, n: Integer;
  1103. begin
  1104. case fg.Mode of
  1105. fgmmTriangles:
  1106. begin
  1107. n := fg.VertexIndices.Count - 3;
  1108. i := 0;
  1109. while i <= n do
  1110. begin
  1111. Writeln(Format('f %d/%0:d %d/%1:d %d/%2:d',
  1112. [fg.VertexIndices[i] + 1 + o,
  1113. fg.VertexIndices[i + 1] + 1 + o,
  1114. fg.VertexIndices[i + 2] + 1 + o]));
  1115. Inc(i, 3);
  1116. end;
  1117. end;
  1118. fgmmTriangleFan:
  1119. begin
  1120. Write('f ');
  1121. n := fg.VertexIndices.Count - 1;
  1122. i := 0;
  1123. while i <= n do
  1124. begin
  1125. if i < n then
  1126. Write(AnsiString(Format('%d/%0:d ', [fg.VertexIndices[i] + 1 + o])))
  1127. else
  1128. Writeln(Format('%d/%0:d', [fg.VertexIndices[i] + 1 + o]));
  1129. Inc(i);
  1130. end;
  1131. end;
  1132. fgmmTriangleStrip:
  1133. begin
  1134. n := fg.VertexIndices.Count - 3;
  1135. i := 0;
  1136. while i <= n do
  1137. begin
  1138. Writeln(Format('f %d/%0:d %d/%1:d %d/%2:d',
  1139. [fg.VertexIndices[i] + 1 + o,
  1140. fg.VertexIndices[i + 1] + 1 + o,
  1141. fg.VertexIndices[i + 2] + 1 + o]));
  1142. Inc(i);
  1143. end;
  1144. end;
  1145. end;
  1146. end;
  1147. procedure WriteFaceGroups;
  1148. var
  1149. j, i, k: Integer;
  1150. fg: TGLFaceGroup;
  1151. MoName: string;
  1152. begin
  1153. k := 0;
  1154. for j := 0 to Owner.MeshObjects.Count - 1 do
  1155. begin
  1156. MoName := Owner.MeshObjects[j].Name;
  1157. if MoName = '' then
  1158. MoName := Format('Mesh%d', [j + 1]);
  1159. Writeln('g ' + MoName);
  1160. for i := 0 to Owner.MeshObjects[j].FaceGroups.Count - 1 do
  1161. begin
  1162. Writeln(Format('s %d', [i + 1]));
  1163. fg := Owner.MeshObjects[j].FaceGroups[i];
  1164. if fg is TOBJFGVertexNormalTexIndexList then
  1165. WriteOBJFaceGroup(TOBJFGVertexNormalTexIndexList(fg), k)
  1166. else if fg is TFGVertexIndexList then
  1167. WriteVertexIndexList(TFGVertexIndexList(fg), k)
  1168. else
  1169. Assert(False); //unsupported face group
  1170. end;
  1171. //advance vertex index offset
  1172. Inc(k, Owner.MeshObjects[j].Vertices.Count);
  1173. end;
  1174. end;
  1175. begin
  1176. Assert(Owner is TGLFreeForm, 'Can only save FreeForms.');
  1177. OldDecimalSeparator := FormatSettings.DecimalSeparator;
  1178. FormatSettings.DecimalSeparator := '.';
  1179. { Better not call anything that wants the system-locale intact
  1180. from this block }
  1181. try
  1182. WriteHeader;
  1183. WriteVertices;
  1184. WriteNormals;
  1185. WriteTexCoords;
  1186. WriteFaceGroups;
  1187. finally
  1188. FormatSettings.DecimalSeparator := OldDecimalSeparator;
  1189. end;
  1190. end;
  1191. // ------------------
  1192. // ------------------ TGLMTLFile ------------------
  1193. // ------------------
  1194. procedure TGLMTLFile.Prepare;
  1195. var
  1196. i: Integer;
  1197. buf: string;
  1198. begin
  1199. // "standardize" the mtl file lines
  1200. for i := Count - 1 downto 0 do
  1201. begin
  1202. buf := UpperCase(Trim(Strings[i]));
  1203. if (buf = '') or CharInSet(buf[1], ['#', '$']) then
  1204. Delete(i)
  1205. else
  1206. begin
  1207. Strings[i] := StringReplace(buf, #9, #32, [rfIgnoreCase]);
  1208. end;
  1209. end;
  1210. end;
  1211. function TGLMTLFile.MaterialStringProperty(const materialName, propertyName: string): string;
  1212. var
  1213. i, n: Integer;
  1214. buf, line: string;
  1215. begin
  1216. buf := 'NEWMTL ' + UpperCase(materialName);
  1217. i := IndexOf(buf);
  1218. if i >= 0 then
  1219. begin
  1220. buf := UpperCase(propertyName) + ' ';
  1221. n := Length(buf);
  1222. Inc(i);
  1223. while i < Count do
  1224. begin
  1225. line := Strings[i];
  1226. if Copy(line, 1, 7) = 'NEWMTL ' then
  1227. Break;
  1228. if Copy(line, 1, n) = buf then
  1229. begin
  1230. Result := Copy(Strings[i], n + 1, MaxInt);
  1231. Exit;
  1232. end;
  1233. Inc(i);
  1234. end;
  1235. end;
  1236. Result := '';
  1237. end;
  1238. function TGLMTLFile.MaterialVectorProperty(const materialName, propertyName: string;
  1239. const defaultValue: TGLVector): TGLVector;
  1240. var
  1241. i: Integer;
  1242. sl: TStringList;
  1243. begin
  1244. sl := TStringList.Create;
  1245. try
  1246. sl.CommaText := MaterialStringProperty(materialName, propertyName);
  1247. if sl.Count > 0 then
  1248. begin
  1249. Result := NullHmgVector;
  1250. for i := 0 to 3 do
  1251. if sl.Count > i then
  1252. Result.V[i] := GLStrToFloatDef(sl[i], 0)
  1253. else
  1254. Break;
  1255. end
  1256. else
  1257. Result := defaultValue;
  1258. finally
  1259. sl.Free;
  1260. end;
  1261. end;
  1262. procedure TOBJFGVertexNormalTexIndexList.Assign(Source: TPersistent);
  1263. begin
  1264. if Source is TOBJFGVertexNormalTexIndexList then
  1265. begin
  1266. FMode := TOBJFGVertexNormalTexIndexList(Source).FMode;
  1267. FName := TOBJFGVertexNormalTexIndexList(Source).FName;
  1268. FCurrentVertexCount := TOBJFGVertexNormalTexIndexList(Source).FCurrentVertexCount;
  1269. FShowNormals := TOBJFGVertexNormalTexIndexList(Source).FShowNormals;
  1270. if TOBJFGVertexNormalTexIndexList(Source).FPolygonVertices = nil then
  1271. FreeAndNil(FPolygonVertices)
  1272. else
  1273. begin
  1274. if FPolygonVertices = nil then
  1275. FPolygonVertices := TGLIntegerList.Create;
  1276. FPolygonVertices.Assign(TOBJFGVertexNormalTexIndexList(Source).FPolygonVertices);
  1277. end;
  1278. end
  1279. else
  1280. inherited;
  1281. end;
  1282. procedure TOBJFGVertexNormalTexIndexList.ReadFromFiler(
  1283. reader: TGLVirtualReader);
  1284. var
  1285. archiveVersion: Integer;
  1286. begin
  1287. inherited ReadFromFiler(reader);
  1288. archiveVersion := reader.ReadInteger;
  1289. if archiveVersion = 0 then
  1290. begin
  1291. FMode := TOBJFGMode(reader.ReadInteger);
  1292. FName := reader.ReadString;
  1293. FCurrentVertexCount := reader.ReadInteger;
  1294. FShowNormals := reader.ReadBoolean;
  1295. if FMode = objfgmmPolygons then
  1296. begin
  1297. FPolygonVertices := TGLIntegerList.Create;
  1298. FPolygonVertices.ReadFromFiler(reader);
  1299. end;
  1300. end
  1301. else
  1302. RaiseFilerException(archiveVersion);
  1303. end;
  1304. procedure TOBJFGVertexNormalTexIndexList.WriteToFiler(
  1305. writer: TGLVirtualWriter);
  1306. begin
  1307. inherited WriteToFiler(writer);
  1308. with writer do
  1309. begin
  1310. WriteInteger(0); // Archive Version 0
  1311. writer.WriteInteger(Ord(FMode));
  1312. writer.WriteString(FName);
  1313. writer.WriteInteger(FCurrentVertexCount);
  1314. writer.WriteBoolean(FShowNormals);
  1315. if FPolygonVertices <> nil then
  1316. FPolygonVertices.WriteToFiler(writer);
  1317. end;
  1318. end;
  1319. //-------------------------------------------------
  1320. initialization
  1321. //-------------------------------------------------
  1322. RegisterVectorFileFormat('obj', 'WaveFront model file', TGLOBJVectorFile);
  1323. RegisterVectorFileFormat('objf', 'Stripe model file', TGLOBJVectorFile);
  1324. RegisterClass(TOBJFGVertexNormalTexIndexList);
  1325. end.