1 // Author: Kirill Gavrilov
2 // Copyright (c) 2017-2019 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include <RWObj_MtlReader.hxx>
17 #include <RWObj_Tools.hxx>
19 #include <Message.hxx>
20 #include <Message_Messenger.hxx>
21 #include <OSD_File.hxx>
22 #include <OSD_OpenFile.hxx>
23 #include <OSD_Path.hxx>
27 //! Try to find a new location of the file relative to specified folder from absolute path.
28 //! @param theAbsolutePath original absolute file path
29 //! @param theNewFoler the new folder to look for the file
30 //! @param theRelativePath result file path relative to theNewFoler
31 //! @return true if relative file has been found
32 static bool findRelativePath (const TCollection_AsciiString& theAbsolutePath,
33 const TCollection_AsciiString& theNewFoler,
34 TCollection_AsciiString& theRelativePath)
36 TCollection_AsciiString aNewFoler = (theNewFoler.EndsWith ("\\") || theNewFoler.EndsWith ("/"))
38 : (theNewFoler + "/");
40 TCollection_AsciiString aRelPath;
41 TCollection_AsciiString aPath = theAbsolutePath;
44 TCollection_AsciiString aFolder, aFileName;
45 OSD_Path::FolderAndFileFromPath (aPath, aFolder, aFileName);
47 || aFileName.IsEmpty())
52 if (aRelPath.IsEmpty())
58 aRelPath = aFileName + "/" + aRelPath;
61 if (OSD_File (aNewFoler + aRelPath).Exists())
63 theRelativePath = aRelPath;
68 for (; aPath.Length() >= 2;)
70 if (aPath.Value (aPath.Length()) == '/'
71 || aPath.Value (aPath.Length()) == '\\')
73 aPath = aPath.SubString (1, aPath.Length() - 1);
82 // =======================================================================
83 // function : RWObj_MtlReader
85 // =======================================================================
86 RWObj_MtlReader::RWObj_MtlReader (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>& theMaterials)
88 myMaterials (&theMaterials),
94 // =======================================================================
95 // function : ~RWObj_MtlReader
97 // =======================================================================
98 RWObj_MtlReader::~RWObj_MtlReader()
106 // =======================================================================
109 // =======================================================================
110 bool RWObj_MtlReader::Read (const TCollection_AsciiString& theFolder,
111 const TCollection_AsciiString& theFile)
113 myPath = theFolder + theFile;
114 myFile = OSD_OpenFile (myPath.ToCString(), "rb");
117 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OBJ material file '") + myPath + "' is not found!", Message_Warning);
118 return Standard_False;
121 char aLine[256] = {};
122 TCollection_AsciiString aMatName;
124 const Standard_Integer aNbMatOld = myMaterials->Extent();
125 bool hasAspect = false;
126 for (; ::feof (myFile) == 0 && ::fgets (aLine, 255, myFile) != NULL; )
130 const char* aPos = aLine;
133 for (; IsSpace(*aPos);)
145 if (::memcmp (aPos, "newmtl", 6) == 0)
148 if (!aMatName.IsEmpty())
152 aMat.Name = aMatName;
156 // reset incomplete material definition
157 aMat = RWObj_Material();
159 myMaterials->Bind (aMatName, aMat);
163 aMatName = TCollection_AsciiString(aPos);
164 aMat = RWObj_Material();
165 if (!RWObj_Tools::ReadName (aPos, aMatName))
167 Message::DefaultMessenger()->Send (TCollection_AsciiString("Empty OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
170 else if (::memcmp (aPos, "Ka", 2) == 0
171 && IsSpace (aPos[2]))
175 Graphic3d_Vec3 aColor;
176 RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
178 if (validateColor (aColor))
180 aMat.AmbientColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB);
184 else if (::memcmp (aPos, "Kd", 2) == 0
185 && IsSpace (aPos[2]))
189 Graphic3d_Vec3 aColor;
190 RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
192 if (validateColor (aColor))
194 aMat.DiffuseColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB);
198 else if (::memcmp (aPos, "Ks", 2) == 0
199 && IsSpace (aPos[2]))
203 Graphic3d_Vec3 aColor;
204 RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
206 if (validateColor (aColor))
208 aMat.SpecularColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB);
212 else if (::memcmp (aPos, "Ns", 2) == 0
213 && IsSpace (aPos[2]))
217 double aSpecular = Strtod (aPos, &aNext);
219 if (aSpecular >= 0.0)
221 aMat.Shininess = (float )Min (aSpecular / 1000.0, 1.0);
225 else if (::memcmp (aPos, "Tr", 2) == 0
226 && IsSpace (aPos[2]))
230 double aTransp = Strtod (aPos, &aNext);
232 if (validateScalar (aTransp)
235 aMat.Transparency = (float )aTransp;
239 else if (*aPos == 'd' && IsSpace (aPos[1]))
244 double anAlpha = Strtod (aPos, &aNext);
246 if (validateScalar (anAlpha)
249 aMat.Transparency = float(1.0 - anAlpha);
253 else if (::memcmp (aPos, "map_Kd", 6) == 0
254 && IsSpace (aPos[6]))
257 if (RWObj_Tools::ReadName (aPos, aMat.DiffuseTexture))
259 processTexturePath (aMat.DiffuseTexture, theFolder);
263 else if (::memcmp (aPos, "map_Ks", 6) == 0
264 && IsSpace (aPos[6]))
267 if (RWObj_Tools::ReadName (aPos, aMat.SpecularTexture))
269 processTexturePath (aMat.SpecularTexture, theFolder);
273 else if (::memcmp (aPos, "map_Bump", 8) == 0
274 && IsSpace (aPos[8]))
277 if (RWObj_Tools::ReadName (aPos, aMat.BumpTexture))
279 processTexturePath (aMat.BumpTexture, theFolder);
283 /*else if (::memcmp (aPos, "illum", 5) == 0)
287 const int aModel = strtol (aPos, &aNext, 10);
289 if (aModel < 0 || aModel > 10)
296 if (!aMatName.IsEmpty())
300 aMat.Name = aMatName;
304 // reset incomplete material definition
305 aMat = RWObj_Material();
307 myMaterials->Bind (aMatName, aMat);
310 return myMaterials->Extent() != aNbMatOld;
313 // =======================================================================
314 // function : processTexturePath
316 // =======================================================================
317 void RWObj_MtlReader::processTexturePath (TCollection_AsciiString& theTexturePath,
318 const TCollection_AsciiString& theFolder)
320 if (OSD_Path::IsAbsolutePath (theTexturePath.ToCString()))
322 Message::DefaultMessenger()->Send (TCollection_AsciiString("OBJ file specifies absolute path to the texture image file which may be inaccessible on another device\n")
323 + theTexturePath, Message_Warning);
324 if (!OSD_File (theTexturePath).Exists())
326 // workaround absolute filenames - try to find the same file at the OBJ file location
327 TCollection_AsciiString aRelativePath;
328 if (findRelativePath (theTexturePath, theFolder, aRelativePath))
330 theTexturePath = theFolder + aRelativePath;
336 theTexturePath = theFolder + theTexturePath;
340 // =======================================================================
341 // function : validateScalar
343 // =======================================================================
344 bool RWObj_MtlReader::validateScalar (const Standard_Real theValue)
349 Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid scalar in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
355 // =======================================================================
356 // function : validateColor
358 // =======================================================================
359 bool RWObj_MtlReader::validateColor (const Graphic3d_Vec3& theVec)
361 if (theVec.r() < 0.0f || theVec.r() > 1.0f
362 || theVec.g() < 0.0f || theVec.g() > 1.0f
363 || theVec.b() < 0.0f || theVec.b() > 1.0f)
365 Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid color in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);