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 | |
25 | namespace |
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 | } |
66 | aPath = aFolder; |
67 | } |
68 | } |
69 | } |
70 | |
71 | // ======================================================================= |
72 | // function : RWObj_MtlReader |
73 | // purpose : |
74 | // ======================================================================= |
75 | RWObj_MtlReader::RWObj_MtlReader (NCollection_DataMap<TCollection_AsciiString, RWObj_Material>& theMaterials) |
76 | : myFile (NULL), |
77 | myMaterials (&theMaterials), |
78 | myNbLines (0) |
79 | { |
80 | // |
81 | } |
82 | |
83 | // ======================================================================= |
84 | // function : ~RWObj_MtlReader |
85 | // purpose : |
86 | // ======================================================================= |
87 | RWObj_MtlReader::~RWObj_MtlReader() |
88 | { |
89 | if (myFile != NULL) |
90 | { |
91 | ::fclose (myFile); |
92 | } |
93 | } |
94 | |
95 | // ======================================================================= |
96 | // function : Read |
97 | // purpose : |
98 | // ======================================================================= |
99 | bool RWObj_MtlReader::Read (const TCollection_AsciiString& theFolder, |
100 | const TCollection_AsciiString& theFile) |
101 | { |
102 | myPath = theFolder + theFile; |
103 | myFile = OSD_OpenFile (myPath.ToCString(), "rb"); |
104 | if (myFile == NULL) |
105 | { |
106 | Message::DefaultMessenger()->Send (TCollection_AsciiString ("OBJ material file '") + myPath + "' is not found!", Message_Warning); |
107 | return Standard_False; |
108 | } |
109 | |
110 | char aLine[256] = {}; |
111 | TCollection_AsciiString aMatName; |
112 | RWObj_Material aMat; |
113 | const Standard_Integer aNbMatOld = myMaterials->Extent(); |
114 | bool hasAspect = false; |
115 | for (; ::feof (myFile) == 0 && ::fgets (aLine, 255, myFile) != NULL; ) |
116 | { |
117 | ++myNbLines; |
118 | |
119 | const char* aPos = aLine; |
120 | |
121 | // skip spaces |
122 | for (; IsSpace(*aPos);) |
123 | { |
124 | ++aPos; |
125 | } |
126 | |
127 | if (*aPos == '#' |
128 | || *aPos == '\n' |
129 | || *aPos == '\0') |
130 | { |
131 | continue; |
132 | } |
133 | |
134 | if (::memcmp (aPos, "newmtl", 6) == 0) |
135 | { |
136 | aPos += 7; |
137 | if (!aMatName.IsEmpty()) |
138 | { |
139 | if (hasAspect) |
140 | { |
141 | aMat.Name = aMatName; |
142 | } |
143 | else |
144 | { |
145 | // reset incomplete material definition |
146 | aMat = RWObj_Material(); |
147 | } |
148 | myMaterials->Bind (aMatName, aMat); |
149 | hasAspect = false; |
150 | } |
151 | |
152 | aMatName = TCollection_AsciiString(aPos); |
153 | aMat = RWObj_Material(); |
154 | if (!RWObj_Tools::ReadName (aPos, aMatName)) |
155 | { |
156 | Message::DefaultMessenger()->Send (TCollection_AsciiString("Empty OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning); |
157 | } |
158 | } |
159 | else if (::memcmp (aPos, "Ka", 2) == 0 |
160 | && IsSpace (aPos[2])) |
161 | { |
162 | aPos += 3; |
163 | char* aNext = NULL; |
164 | Graphic3d_Vec3 aColor; |
165 | RWObj_Tools::ReadVec3 (aPos, aNext, aColor); |
166 | aPos = aNext; |
167 | if (validateColor (aColor)) |
168 | { |
169 | aMat.AmbientColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB); |
170 | hasAspect = true; |
171 | } |
172 | } |
173 | else if (::memcmp (aPos, "Kd", 2) == 0 |
174 | && IsSpace (aPos[2])) |
175 | { |
176 | aPos += 3; |
177 | char* aNext = NULL; |
178 | Graphic3d_Vec3 aColor; |
179 | RWObj_Tools::ReadVec3 (aPos, aNext, aColor); |
180 | aPos = aNext; |
181 | if (validateColor (aColor)) |
182 | { |
183 | aMat.DiffuseColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB); |
184 | hasAspect = true; |
185 | } |
186 | } |
187 | else if (::memcmp (aPos, "Ks", 2) == 0 |
188 | && IsSpace (aPos[2])) |
189 | { |
190 | aPos += 3; |
191 | char* aNext = NULL; |
192 | Graphic3d_Vec3 aColor; |
193 | RWObj_Tools::ReadVec3 (aPos, aNext, aColor); |
194 | aPos = aNext; |
195 | if (validateColor (aColor)) |
196 | { |
197 | aMat.SpecularColor = Quantity_Color (aColor.r(), aColor.g(), aColor.b(), Quantity_TOC_RGB); |
198 | } |
199 | } |
200 | else if (::memcmp (aPos, "Ns", 2) == 0 |
201 | && IsSpace (aPos[2])) |
202 | { |
203 | aPos += 3; |
204 | char* aNext = NULL; |
205 | double aSpecular = Strtod (aPos, &aNext); |
206 | aPos = aNext; |
207 | if (aSpecular >= 0.0) |
208 | { |
209 | aMat.Shininess = (float )Min (aSpecular / 1000.0, 1.0); |
210 | } |
211 | } |
212 | else if (::memcmp (aPos, "Tr", 2) == 0 |
213 | && IsSpace (aPos[2])) |
214 | { |
215 | aPos += 3; |
216 | char* aNext = NULL; |
217 | double aTransp = Strtod (aPos, &aNext); |
218 | aPos = aNext; |
219 | if (validateScalar (aTransp) |
220 | && aTransp <= 0.99) |
221 | { |
222 | aMat.Transparency = (float )aTransp; |
223 | } |
224 | } |
225 | else if (*aPos == 'd' && IsSpace (aPos[1])) |
226 | { |
227 | // dissolve |
228 | aPos += 2; |
229 | char* aNext = NULL; |
230 | double anAlpha = Strtod (aPos, &aNext); |
231 | aPos = aNext; |
232 | if (validateScalar (anAlpha) |
233 | && anAlpha >= 0.01) |
234 | { |
235 | aMat.Transparency = float(1.0 - anAlpha); |
236 | } |
237 | } |
238 | else if (::memcmp (aPos, "map_Kd", 6) == 0 |
239 | && IsSpace (aPos[6])) |
240 | { |
241 | aPos += 7; |
242 | if (RWObj_Tools::ReadName (aPos, aMat.DiffuseTexture)) |
243 | { |
244 | processTexturePath (aMat.DiffuseTexture, theFolder); |
245 | } |
246 | } |
247 | else if (::memcmp (aPos, "map_Ks", 6) == 0 |
248 | && IsSpace (aPos[6])) |
249 | { |
250 | aPos += 7; |
251 | if (RWObj_Tools::ReadName (aPos, aMat.SpecularTexture)) |
252 | { |
253 | processTexturePath (aMat.SpecularTexture, theFolder); |
254 | } |
255 | } |
256 | else if (::memcmp (aPos, "map_Bump", 8) == 0 |
257 | && IsSpace (aPos[8])) |
258 | { |
259 | aPos += 9; |
260 | if (RWObj_Tools::ReadName (aPos, aMat.BumpTexture)) |
261 | { |
262 | processTexturePath (aMat.BumpTexture, theFolder); |
263 | } |
264 | } |
265 | /*else if (::memcmp (aPos, "illum", 5) == 0) |
266 | { |
267 | aPos += 6; |
268 | char* aNext = NULL; |
269 | const int aModel = strtol (aPos, &aNext, 10); |
270 | aPos = aNext; |
271 | if (aModel < 0 || aModel > 10) |
272 | { |
273 | // unknown model |
274 | } |
275 | }*/ |
276 | } |
277 | |
278 | if (!aMatName.IsEmpty()) |
279 | { |
280 | if (hasAspect) |
281 | { |
282 | aMat.Name = aMatName; |
283 | } |
284 | else |
285 | { |
286 | // reset incomplete material definition |
287 | aMat = RWObj_Material(); |
288 | } |
289 | myMaterials->Bind (aMatName, aMat); |
290 | } |
291 | |
292 | return myMaterials->Extent() != aNbMatOld; |
293 | } |
294 | |
295 | // ======================================================================= |
296 | // function : processTexturePath |
297 | // purpose : |
298 | // ======================================================================= |
299 | void RWObj_MtlReader::processTexturePath (TCollection_AsciiString& theTexturePath, |
300 | const TCollection_AsciiString& theFolder) |
301 | { |
302 | if (OSD_Path::IsAbsolutePath (theTexturePath.ToCString())) |
303 | { |
304 | Message::DefaultMessenger()->Send (TCollection_AsciiString("OBJ file specifies absolute path to the texture image file which may be inaccessible on another device\n") |
305 | + theTexturePath, Message_Warning); |
306 | if (!OSD_File (theTexturePath).Exists()) |
307 | { |
308 | // workaround absolute filenames - try to find the same file at the OBJ file location |
309 | TCollection_AsciiString aRelativePath; |
310 | if (findRelativePath (theTexturePath, theFolder, aRelativePath)) |
311 | { |
312 | theTexturePath = theFolder + aRelativePath; |
313 | } |
314 | } |
315 | } |
316 | else |
317 | { |
318 | theTexturePath = theFolder + theTexturePath; |
319 | } |
320 | } |
321 | |
322 | // ======================================================================= |
323 | // function : validateScalar |
324 | // purpose : |
325 | // ======================================================================= |
326 | bool RWObj_MtlReader::validateScalar (const Standard_Real theValue) |
327 | { |
328 | if (theValue < 0.0 |
329 | || theValue > 1.0) |
330 | { |
331 | Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid scalar in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning); |
332 | return false; |
333 | } |
334 | return true; |
335 | } |
336 | |
337 | // ======================================================================= |
338 | // function : validateColor |
339 | // purpose : |
340 | // ======================================================================= |
341 | bool RWObj_MtlReader::validateColor (const Graphic3d_Vec3& theVec) |
342 | { |
343 | if (theVec.r() < 0.0f || theVec.r() > 1.0f |
344 | || theVec.g() < 0.0f || theVec.g() > 1.0f |
345 | || theVec.b() < 0.0f || theVec.b() > 1.0f) |
346 | { |
347 | Message::DefaultMessenger()->Send (TCollection_AsciiString("Invalid color in OBJ material at line ") + myNbLines + " in file " + myPath, Message_Warning); |
348 | return false; |
349 | } |
350 | return true; |
351 | } |