0031124: Configuration - linker errors when building with CLang on Windows
[occt.git] / src / RWObj / RWObj_MtlReader.cxx
CommitLineData
4151c94d 1// Author: Kirill Gavrilov
2// Copyright (c) 2017-2019 OPEN CASCADE SAS
3//
4// This file is part of Open CASCADE Technology software library.
5//
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.
11//
12// Alternatively, this file may be used under the terms of Open CASCADE
13// commercial license or contractual agreement.
14
15#include <RWObj_MtlReader.hxx>
16
17#include <RWObj_Tools.hxx>
18
19#include <Message.hxx>
20#include <Message_Messenger.hxx>
21#include <OSD_File.hxx>
22#include <OSD_OpenFile.hxx>
23#include <OSD_Path.hxx>
24
25namespace
26{
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)
35 {
36 TCollection_AsciiString aNewFoler = (theNewFoler.EndsWith ("\\") || theNewFoler.EndsWith ("/"))
37 ? theNewFoler
38 : (theNewFoler + "/");
39
40 TCollection_AsciiString aRelPath;
41 TCollection_AsciiString aPath = theAbsolutePath;
42 for (;;)
43 {
44 TCollection_AsciiString aFolder, aFileName;
45 OSD_Path::FolderAndFileFromPath (aPath, aFolder, aFileName);
46 if (aFolder.IsEmpty()
47 || aFileName.IsEmpty())
48 {
49 return false;
50 }
51
52 if (aRelPath.IsEmpty())
53 {
54 aRelPath = aFileName;
55 }
56 else
57 {
58 aRelPath = aFileName + "/" + aRelPath;
59 }
60
61 if (OSD_File (aNewFoler + aRelPath).Exists())
62 {
63 theRelativePath = aRelPath;
64 return true;
65 }
d9dd0754 66
4151c94d 67 aPath = aFolder;
d9dd0754 68 for (; aPath.Length() >= 2;)
69 {
70 if (aPath.Value (aPath.Length()) == '/'
71 || aPath.Value (aPath.Length()) == '\\')
72 {
73 aPath = aPath.SubString (1, aPath.Length() - 1);
74 continue;
75 }
76 break;
77 }
4151c94d 78 }
79 }
80}
81
82// =======================================================================
83// function : RWObj_MtlReader
84// purpose :
85// =======================================================================
86RWObj_MtlReader::RWObj_MtlReader (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>& theMaterials)
87: myFile (NULL),
88 myMaterials (&theMaterials),
89 myNbLines (0)
90{
91 //
92}
93
94// =======================================================================
95// function : ~RWObj_MtlReader
96// purpose :
97// =======================================================================
98RWObj_MtlReader::~RWObj_MtlReader()
99{
100 if (myFile != NULL)
101 {
102 ::fclose (myFile);
103 }
104}
105
106// =======================================================================
107// function : Read
108// purpose :
109// =======================================================================
110bool RWObj_MtlReader::Read (const TCollection_AsciiString& theFolder,
111 const TCollection_AsciiString& theFile)
112{
113 myPath = theFolder + theFile;
114 myFile = OSD_OpenFile (myPath.ToCString(), "rb");
115 if (myFile == NULL)
116 {
117 Message::DefaultMessenger()->Send (TCollection_AsciiString ("OBJ material file '") + myPath + "' is not found!", Message_Warning);
118 return Standard_False;
119 }
120
121 char aLine[256] = {};
122 TCollection_AsciiString aMatName;
123 RWObj_Material aMat;
124 const Standard_Integer aNbMatOld = myMaterials->Extent();
125 bool hasAspect = false;
126 for (; ::feof (myFile) == 0 && ::fgets (aLine, 255, myFile) != NULL; )
127 {
128 ++myNbLines;
129
130 const char* aPos = aLine;
131
132 // skip spaces
133 for (; IsSpace(*aPos);)
134 {
135 ++aPos;
136 }
137
138 if (*aPos == '#'
139 || *aPos == '\n'
140 || *aPos == '\0')
141 {
142 continue;
143 }
144
145 if (::memcmp (aPos, "newmtl", 6) == 0)
146 {
147 aPos += 7;
148 if (!aMatName.IsEmpty())
149 {
150 if (hasAspect)
151 {
152 aMat.Name = aMatName;
153 }
154 else
155 {
156 // reset incomplete material definition
157 aMat = RWObj_Material();
158 }
159 myMaterials->Bind (aMatName, aMat);
160 hasAspect = false;
161 }
162
163 aMatName = TCollection_AsciiString(aPos);
164 aMat = RWObj_Material();
165 if (!RWObj_Tools::ReadName (aPos, aMatName))
166 {
167 Message::DefaultMessenger()->Send (TCollection_AsciiString("Empty OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
168 }
169 }
170 else if (::memcmp (aPos, "Ka", 2) == 0
171 && IsSpace (aPos[2]))
172 {
173 aPos += 3;
174 char* aNext = NULL;
175 Graphic3d_Vec3 aColor;
176 RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
177 aPos = aNext;
178 if (validateColor (aColor))
179 {
ba00aab7 180 aMat.AmbientColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_sRGB);
4151c94d 181 hasAspect = true;
182 }
183 }
184 else if (::memcmp (aPos, "Kd", 2) == 0
185 && IsSpace (aPos[2]))
186 {
187 aPos += 3;
188 char* aNext = NULL;
189 Graphic3d_Vec3 aColor;
190 RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
191 aPos = aNext;
192 if (validateColor (aColor))
193 {
ba00aab7 194 aMat.DiffuseColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_sRGB);
4151c94d 195 hasAspect = true;
196 }
197 }
198 else if (::memcmp (aPos, "Ks", 2) == 0
199 && IsSpace (aPos[2]))
200 {
201 aPos += 3;
202 char* aNext = NULL;
203 Graphic3d_Vec3 aColor;
204 RWObj_Tools::ReadVec3 (aPos, aNext, aColor);
205 aPos = aNext;
206 if (validateColor (aColor))
207 {
ba00aab7 208 aMat.SpecularColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_sRGB);
d9dd0754 209 hasAspect = true;
4151c94d 210 }
211 }
212 else if (::memcmp (aPos, "Ns", 2) == 0
213 && IsSpace (aPos[2]))
214 {
215 aPos += 3;
216 char* aNext = NULL;
217 double aSpecular = Strtod (aPos, &aNext);
218 aPos = aNext;
219 if (aSpecular >= 0.0)
220 {
221 aMat.Shininess = (float )Min (aSpecular / 1000.0, 1.0);
d9dd0754 222 hasAspect = true;
4151c94d 223 }
224 }
225 else if (::memcmp (aPos, "Tr", 2) == 0
226 && IsSpace (aPos[2]))
227 {
228 aPos += 3;
229 char* aNext = NULL;
230 double aTransp = Strtod (aPos, &aNext);
231 aPos = aNext;
232 if (validateScalar (aTransp)
233 && aTransp <= 0.99)
234 {
235 aMat.Transparency = (float )aTransp;
d9dd0754 236 hasAspect = true;
4151c94d 237 }
238 }
239 else if (*aPos == 'd' && IsSpace (aPos[1]))
240 {
241 // dissolve
242 aPos += 2;
243 char* aNext = NULL;
244 double anAlpha = Strtod (aPos, &aNext);
245 aPos = aNext;
246 if (validateScalar (anAlpha)
247 && anAlpha >= 0.01)
248 {
249 aMat.Transparency = float(1.0 - anAlpha);
d9dd0754 250 hasAspect = true;
4151c94d 251 }
252 }
253 else if (::memcmp (aPos, "map_Kd", 6) == 0
254 && IsSpace (aPos[6]))
255 {
256 aPos += 7;
257 if (RWObj_Tools::ReadName (aPos, aMat.DiffuseTexture))
258 {
259 processTexturePath (aMat.DiffuseTexture, theFolder);
d9dd0754 260 hasAspect = true;
4151c94d 261 }
262 }
263 else if (::memcmp (aPos, "map_Ks", 6) == 0
264 && IsSpace (aPos[6]))
265 {
266 aPos += 7;
267 if (RWObj_Tools::ReadName (aPos, aMat.SpecularTexture))
268 {
269 processTexturePath (aMat.SpecularTexture, theFolder);
d9dd0754 270 hasAspect = true;
4151c94d 271 }
272 }
273 else if (::memcmp (aPos, "map_Bump", 8) == 0
274 && IsSpace (aPos[8]))
275 {
276 aPos += 9;
277 if (RWObj_Tools::ReadName (aPos, aMat.BumpTexture))
278 {
279 processTexturePath (aMat.BumpTexture, theFolder);
d9dd0754 280 hasAspect = true;
4151c94d 281 }
282 }
283 /*else if (::memcmp (aPos, "illum", 5) == 0)
284 {
285 aPos += 6;
286 char* aNext = NULL;
287 const int aModel = strtol (aPos, &aNext, 10);
288 aPos = aNext;
289 if (aModel < 0 || aModel > 10)
290 {
291 // unknown model
292 }
293 }*/
294 }
295
296 if (!aMatName.IsEmpty())
297 {
298 if (hasAspect)
299 {
300 aMat.Name = aMatName;
301 }
302 else
303 {
304 // reset incomplete material definition
305 aMat = RWObj_Material();
306 }
307 myMaterials->Bind (aMatName, aMat);
308 }
309
310 return myMaterials->Extent() != aNbMatOld;
311}
312
313// =======================================================================
314// function : processTexturePath
315// purpose :
316// =======================================================================
317void RWObj_MtlReader::processTexturePath (TCollection_AsciiString& theTexturePath,
318 const TCollection_AsciiString& theFolder)
319{
320 if (OSD_Path::IsAbsolutePath (theTexturePath.ToCString()))
321 {
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())
325 {
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))
329 {
330 theTexturePath = theFolder + aRelativePath;
331 }
332 }
333 }
334 else
335 {
336 theTexturePath = theFolder + theTexturePath;
337 }
338}
339
340// =======================================================================
341// function : validateScalar
342// purpose :
343// =======================================================================
344bool RWObj_MtlReader::validateScalar (const Standard_Real theValue)
345{
346 if (theValue < 0.0
347 || theValue > 1.0)
348 {
349 Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid scalar in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
350 return false;
351 }
352 return true;
353}
354
355// =======================================================================
356// function : validateColor
357// purpose :
358// =======================================================================
359bool RWObj_MtlReader::validateColor (const Graphic3d_Vec3& theVec)
360{
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)
364 {
365 Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid color in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning);
366 return false;
367 }
368 return true;
369}