/src/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2026, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use of this software in source and binary forms, |
11 | | with or without modification, are permitted provided that the following |
12 | | conditions are met: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
18 | | * Redistributions in binary form must reproduce the above |
19 | | copyright notice, this list of conditions and the |
20 | | following disclaimer in the documentation and/or other |
21 | | materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the assimp team, nor the names of its |
24 | | contributors may be used to endorse or promote products |
25 | | derived from this software without specific prior |
26 | | written permission of the assimp team. |
27 | | |
28 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | --------------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER |
43 | | |
44 | | #include "ObjFileMtlImporter.h" |
45 | | #include "ObjFileData.h" |
46 | | #include "ObjTools.h" |
47 | | #include <assimp/DefaultIOSystem.h> |
48 | | #include <assimp/ParsingUtils.h> |
49 | | #include <assimp/fast_atof.h> |
50 | | #include <assimp/material.h> |
51 | | #include <stdlib.h> |
52 | | #include <assimp/DefaultLogger.hpp> |
53 | | |
54 | | namespace Assimp { |
55 | | |
56 | | // Material specific token (case insensitive compare) |
57 | | static constexpr char DiffuseTexture[] = "map_Kd"; |
58 | | static constexpr char AmbientTexture[] = "map_Ka"; |
59 | | static constexpr char SpecularTexture[] = "map_Ks"; |
60 | | static constexpr char OpacityTexture[] = "map_d"; |
61 | | static constexpr char EmissiveTexture1[] = "map_emissive"; |
62 | | static constexpr char EmissiveTexture2[] = "map_Ke"; |
63 | | static constexpr char BumpTexture1[] = "map_bump"; |
64 | | static constexpr char BumpTexture2[] = "bump"; |
65 | | static constexpr char NormalTextureV1[] = "map_Kn"; |
66 | | static constexpr char NormalTextureV2[] = "norm"; |
67 | | static constexpr char ReflectionTexture[] = "refl"; |
68 | | static constexpr char DisplacementTexture1[] = "map_disp"; |
69 | | static constexpr char DisplacementTexture2[] = "disp"; |
70 | | static constexpr char SpecularityTexture[] = "map_ns"; |
71 | | static constexpr char RoughnessTexture[] = "map_Pr"; |
72 | | static constexpr char MetallicTexture[] = "map_Pm"; |
73 | | static constexpr char SheenTexture[] = "map_Ps"; |
74 | | static constexpr char RMATexture[] = "map_Ps"; |
75 | | |
76 | | // texture option specific token |
77 | | static constexpr char BlendUOption[] = "-blendu"; |
78 | | static constexpr char BlendVOption[] = "-blendv"; |
79 | | static constexpr char BoostOption[] = "-boost"; |
80 | | static constexpr char ModifyMapOption[] = "-mm"; |
81 | | static constexpr char OffsetOption[] = "-o"; |
82 | | static constexpr char ScaleOption[] = "-s"; |
83 | | static constexpr char TurbulenceOption[] = "-t"; |
84 | | static constexpr char ResolutionOption[] = "-texres"; |
85 | | static constexpr char ClampOption[] = "-clamp"; |
86 | | static constexpr char BumpOption[] = "-bm"; |
87 | | static constexpr char ChannelOption[] = "-imfchan"; |
88 | | static constexpr char TypeOption[] = "-type"; |
89 | | |
90 | | // ------------------------------------------------------------------- |
91 | | // Constructor |
92 | | ObjFileMtlImporter::ObjFileMtlImporter(std::vector<char> &buffer, |
93 | | const std::string &strAbsPath, |
94 | | ObjFile::Model *pModel) : |
95 | 66.0k | m_strAbsPath(strAbsPath), |
96 | 66.0k | m_DataIt(buffer.begin()), |
97 | 66.0k | m_DataItEnd(buffer.end()), |
98 | 66.0k | m_pModel(pModel), |
99 | 66.0k | m_uiLine(0), |
100 | 66.0k | m_buffer() { |
101 | 66.0k | ai_assert(nullptr != m_pModel); |
102 | 66.0k | m_buffer.resize(BUFFERSIZE); |
103 | 66.0k | std::fill(m_buffer.begin(), m_buffer.end(), '\0'); |
104 | 66.0k | if (nullptr == m_pModel->mDefaultMaterial) { |
105 | 0 | m_pModel->mDefaultMaterial = new ObjFile::Material; |
106 | 0 | m_pModel->mDefaultMaterial->MaterialName.Set("default"); |
107 | 0 | } |
108 | | |
109 | | // Try with OS folder separator first |
110 | 66.0k | char folderSeparator = DefaultIOSystem().getOsSeparator(); |
111 | 66.0k | std::size_t found = m_strAbsPath.find_last_of(folderSeparator); |
112 | 66.0k | if (found == std::string::npos) { |
113 | | // Not found, try alternative folder separator |
114 | 54.1k | folderSeparator = (folderSeparator == '/' ? '\\' : '/'); |
115 | 54.1k | found = m_strAbsPath.find_last_of(folderSeparator); |
116 | 54.1k | } |
117 | 66.0k | if (found != std::string::npos) { |
118 | 12.6k | m_strAbsPath = m_strAbsPath.substr(0, found + 1); |
119 | 53.4k | } else { |
120 | 53.4k | m_strAbsPath = ""; |
121 | 53.4k | } |
122 | 66.0k | load(); |
123 | 66.0k | } |
124 | | |
125 | | // ------------------------------------------------------------------- |
126 | | // Loads the material description |
127 | 66.0k | void ObjFileMtlImporter::load() { |
128 | 66.0k | if (m_DataIt == m_DataItEnd) |
129 | 0 | return; |
130 | | |
131 | 522M | while (m_DataIt != m_DataItEnd) { |
132 | 522M | switch (*m_DataIt) { |
133 | 313k | case 'k': |
134 | 1.05M | case 'K': { |
135 | 1.05M | ++m_DataIt; |
136 | 1.05M | if (*m_DataIt == 'a') // Ambient color |
137 | 262k | { |
138 | 262k | ++m_DataIt; |
139 | 262k | if (m_pModel->mCurrentMaterial != nullptr) |
140 | 260k | getColorRGBA(&m_pModel->mCurrentMaterial->ambient); |
141 | 791k | } else if (*m_DataIt == 'd') { |
142 | | // Diffuse color |
143 | 328k | ++m_DataIt; |
144 | 328k | if (m_pModel->mCurrentMaterial != nullptr) |
145 | 324k | getColorRGBA(&m_pModel->mCurrentMaterial->diffuse); |
146 | 463k | } else if (*m_DataIt == 's') { |
147 | 137k | ++m_DataIt; |
148 | 137k | if (m_pModel->mCurrentMaterial != nullptr) |
149 | 136k | getColorRGBA(&m_pModel->mCurrentMaterial->specular); |
150 | 325k | } else if (*m_DataIt == 'e') { |
151 | 1.02k | ++m_DataIt; |
152 | 1.02k | if (m_pModel->mCurrentMaterial != nullptr) |
153 | 378 | getColorRGBA(&m_pModel->mCurrentMaterial->emissive); |
154 | 1.02k | } |
155 | 1.05M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
156 | 1.05M | } break; |
157 | 85.1k | case 'T': { |
158 | 85.1k | ++m_DataIt; |
159 | | // Material transmission color |
160 | 85.1k | if (*m_DataIt == 'f') { |
161 | 585 | ++m_DataIt; |
162 | 585 | if (m_pModel->mCurrentMaterial != nullptr) |
163 | 484 | getColorRGBA(&m_pModel->mCurrentMaterial->transparent); |
164 | 84.5k | } else if (*m_DataIt == 'r') { |
165 | | // Material transmission alpha value |
166 | 636 | ++m_DataIt; |
167 | 636 | ai_real d; |
168 | 636 | getFloatValue(d); |
169 | 636 | if (m_pModel->mCurrentMaterial != nullptr) |
170 | 593 | m_pModel->mCurrentMaterial->alpha = static_cast<ai_real>(1.0) - d; |
171 | 636 | } |
172 | 85.1k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
173 | 85.1k | } break; |
174 | 308k | case 'd': { |
175 | 308k | if (*(m_DataIt + 1) == 'i' && *(m_DataIt + 2) == 's' && *(m_DataIt + 3) == 'p') { |
176 | | // A displacement map |
177 | 44.7k | getTexture(); |
178 | 263k | } else { |
179 | | // Alpha value |
180 | 263k | ++m_DataIt; |
181 | 263k | if (m_pModel->mCurrentMaterial != nullptr) |
182 | 232k | getFloatValue(m_pModel->mCurrentMaterial->alpha); |
183 | 263k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
184 | 263k | } |
185 | 308k | } break; |
186 | | |
187 | 324k | case 'N': |
188 | 1.32M | case 'n': { |
189 | 1.32M | ++m_DataIt; |
190 | 1.32M | switch (*m_DataIt) { |
191 | 137k | case 's': // Specular exponent |
192 | 137k | ++m_DataIt; |
193 | 137k | if (m_pModel->mCurrentMaterial != nullptr) |
194 | 137k | getFloatValue(m_pModel->mCurrentMaterial->shineness); |
195 | 137k | break; |
196 | 1.09k | case 'i': // Index Of refraction |
197 | 1.09k | ++m_DataIt; |
198 | 1.09k | if (m_pModel->mCurrentMaterial != nullptr) |
199 | 701 | getFloatValue(m_pModel->mCurrentMaterial->ior); |
200 | 1.09k | break; |
201 | 495k | case 'e': // New material |
202 | 495k | createMaterial(); |
203 | 495k | break; |
204 | 83.7k | case 'o': // Norm texture |
205 | 83.7k | --m_DataIt; |
206 | 83.7k | getTexture(); |
207 | 83.7k | break; |
208 | 1.32M | } |
209 | 1.32M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
210 | 1.32M | } break; |
211 | | |
212 | 465k | case 'P': |
213 | 465k | { |
214 | 465k | ++m_DataIt; |
215 | 465k | switch(*m_DataIt) |
216 | 465k | { |
217 | 1.62k | case 'r': |
218 | 1.62k | ++m_DataIt; |
219 | 1.62k | if (m_pModel->mCurrentMaterial != nullptr) |
220 | 1.28k | getFloatValue(m_pModel->mCurrentMaterial->roughness); |
221 | 1.62k | break; |
222 | 6.36k | case 'm': |
223 | 6.36k | ++m_DataIt; |
224 | 6.36k | if (m_pModel->mCurrentMaterial != nullptr) |
225 | 6.06k | getFloatValue(m_pModel->mCurrentMaterial->metallic); |
226 | 6.36k | break; |
227 | 709 | case 's': |
228 | 709 | ++m_DataIt; |
229 | 709 | if (m_pModel->mCurrentMaterial != nullptr) |
230 | 674 | getColorRGBA(m_pModel->mCurrentMaterial->sheen); |
231 | 709 | break; |
232 | 586 | case 'c': |
233 | 586 | ++m_DataIt; |
234 | 586 | if (*m_DataIt == 'r') { |
235 | 0 | ++m_DataIt; |
236 | 0 | if (m_pModel->mCurrentMaterial != nullptr) |
237 | 0 | getFloatValue(m_pModel->mCurrentMaterial->clearcoat_roughness); |
238 | 586 | } else if (*m_DataIt == 't') { |
239 | 0 | ++m_DataIt; |
240 | 0 | if (m_pModel->mCurrentMaterial != nullptr) |
241 | 0 | getFloatValue(m_pModel->mCurrentMaterial->clearcoat_thickness); |
242 | 586 | } else { |
243 | 586 | if (m_pModel->mCurrentMaterial != nullptr) |
244 | 583 | getFloatValue(m_pModel->mCurrentMaterial->clearcoat); |
245 | 586 | } |
246 | 586 | break; |
247 | 465k | } |
248 | 465k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
249 | 465k | } |
250 | 0 | break; |
251 | | |
252 | 15.7M | case 'm': // Texture or metallic |
253 | 15.7M | { |
254 | | // Save start of token (after 'm') |
255 | 15.7M | auto tokenStart = m_DataIt; // points to 'm' |
256 | 15.7M | auto tokenEnd = getNextDelimiter(m_DataIt, m_DataItEnd); // move iterator to end of token |
257 | | |
258 | 15.7M | std::string keyword(tokenStart, tokenEnd); |
259 | 15.7M | m_DataIt = getNextWord(tokenEnd, m_DataItEnd); // advance iterator |
260 | | |
261 | 15.7M | if (keyword.compare(0, 3, "map") == 0) { |
262 | | // starts with "map", treat as texture map |
263 | 424k | m_DataIt = tokenStart; |
264 | 424k | getTexture(); |
265 | 15.3M | } else if (keyword == "metallic" || keyword == "metal" || keyword == "metalness") { |
266 | | // parse metallic float value instead of texture |
267 | 7.01k | getFloatIfMaterialValid(&ObjFile::Material::metallic); |
268 | 7.01k | } |
269 | | |
270 | 15.7M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
271 | 15.7M | } break; |
272 | | |
273 | 277k | case 'b': // quick'n'dirty - for 'bump' sections |
274 | 277k | { |
275 | 277k | getTexture(); |
276 | 277k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
277 | 277k | } break; |
278 | | |
279 | 1.51M | case 'r': // refl (map) or roughness (float) |
280 | 1.51M | { |
281 | 1.51M | auto tokenStart = m_DataIt; // points to 'r' |
282 | 1.51M | auto tokenEnd = getNextDelimiter(m_DataIt, m_DataItEnd); |
283 | 1.51M | std::string keyword(tokenStart, tokenEnd); |
284 | 1.51M | m_DataIt = getNextWord(tokenEnd, m_DataItEnd); |
285 | | |
286 | 1.51M | if (keyword == "roughness" || keyword == "rough") { |
287 | 417 | getFloatIfMaterialValid(&ObjFile::Material::roughness); |
288 | 1.51M | } else if (keyword == "refl" || keyword == "reflection") { |
289 | 819 | m_DataIt = tokenStart; |
290 | 819 | getTexture(); |
291 | 819 | } |
292 | | |
293 | 1.51M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
294 | 1.51M | } break; |
295 | | |
296 | 1.78M | case 'i': // Illumination model |
297 | 1.78M | { |
298 | 1.78M | m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
299 | 1.78M | if (m_pModel->mCurrentMaterial != nullptr) |
300 | 1.70M | getIlluminationModel(m_pModel->mCurrentMaterial->illumination_model); |
301 | 1.78M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
302 | 1.78M | } break; |
303 | | |
304 | 2.51M | case 'a': { |
305 | 2.51M | auto tokenStart = m_DataIt; |
306 | 2.51M | auto tokenEnd = getNextDelimiter(m_DataIt, m_DataItEnd); |
307 | 2.51M | std::string keyword(tokenStart, tokenEnd); |
308 | 2.51M | m_DataIt = getNextWord(tokenEnd, m_DataItEnd); |
309 | | |
310 | 2.51M | if (keyword == "aniso" || keyword == "anisotropy") { |
311 | 31.6k | getFloatIfMaterialValid(&ObjFile::Material::anisotropy); |
312 | 2.48M | } else if (keyword == "ao") { |
313 | 2.85k | getFloatIfMaterialValid(&ObjFile::Material::ambient_occlusion); |
314 | 2.47M | } else if (keyword == "anisor" || ai_stdStrToLower(keyword) == "anisotropicrotation") { |
315 | 284 | getFloatIfMaterialValid(&ObjFile::Material::anisotropy_rotation); |
316 | 2.47M | } else { |
317 | 2.47M | ASSIMP_LOG_WARN("Unhandled keyword: ", keyword ); |
318 | 2.47M | } |
319 | | |
320 | 2.51M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
321 | 2.51M | } break; |
322 | | |
323 | 2.15M | case 's': { |
324 | 2.15M | auto tokenStart = m_DataIt; |
325 | 2.15M | auto tokenEnd = getNextDelimiter(m_DataIt, m_DataItEnd); |
326 | 2.15M | std::string keyword(tokenStart, tokenEnd); |
327 | 2.15M | m_DataIt = getNextWord(tokenEnd,m_DataItEnd); |
328 | | |
329 | 2.15M | if (keyword == "subsurface" || keyword == "scattering") { |
330 | 12.2k | getFloatIfMaterialValid(&ObjFile::Material::subsurface_scattering); |
331 | 2.14M | } else if (ai_stdStrToLower(keyword) == "speculartint") { |
332 | 0 | getFloatIfMaterialValid(&ObjFile::Material::specular_tint); |
333 | 2.14M | } else if (keyword == "sheen") { |
334 | 413k | getFloatIfMaterialValid(&ObjFile::Material::sheen_grazing); |
335 | 1.73M | } else if (ai_stdStrToLower(keyword) == "sheentint") { |
336 | 1.16k | getFloatIfMaterialValid(&ObjFile::Material::sheen_tint); |
337 | 1.73M | } else { |
338 | 1.73M | ASSIMP_LOG_WARN("Unhandled keyword: ", keyword ); |
339 | 1.73M | } |
340 | | |
341 | 2.15M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
342 | 2.15M | } break; |
343 | | |
344 | 632k | case 'c': { |
345 | 632k | auto tokenStart = m_DataIt; |
346 | 632k | auto tokenEnd = getNextDelimiter(m_DataIt, m_DataItEnd); |
347 | 632k | std::string keyword(tokenStart, tokenEnd); |
348 | 632k | m_DataIt = getNextWord(tokenEnd, m_DataItEnd); |
349 | | |
350 | 632k | if (ai_stdStrToLower(keyword) == "clearcoat") { |
351 | 2.85k | getFloatIfMaterialValid(&ObjFile::Material::clearcoat); |
352 | 630k | } else if (ai_stdStrToLower(keyword) == "clearcoatgloss") { |
353 | 0 | getFloatIfMaterialValid(&ObjFile::Material::clearcoat_gloss); |
354 | 630k | } else { |
355 | 630k | ASSIMP_LOG_WARN("Unhandled keyword: ", keyword ); |
356 | 630k | } |
357 | | |
358 | 632k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
359 | 632k | } break; |
360 | | |
361 | 494M | default: { |
362 | 494M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
363 | 494M | } break; |
364 | 522M | } |
365 | 522M | } |
366 | 66.0k | } |
367 | | |
368 | | // ------------------------------------------------------------------- |
369 | | // Loads a color definition |
370 | 723k | void ObjFileMtlImporter::getColorRGBA(aiColor3D *pColor) { |
371 | 723k | ai_assert(nullptr != pColor); |
372 | | |
373 | 723k | ai_real r(0.0), g(0.0), b(0.0); |
374 | 723k | m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, r); |
375 | 723k | pColor->r = r; |
376 | | |
377 | | // we have to check if color is default 0 with only one token |
378 | 723k | if (!IsLineEnd(*m_DataIt)) { |
379 | 659k | m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, g); |
380 | 659k | m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, b); |
381 | 659k | } |
382 | 723k | pColor->g = g; |
383 | 723k | pColor->b = b; |
384 | 723k | } |
385 | | |
386 | | // ------------------------------------------------------------------- |
387 | 674 | void ObjFileMtlImporter::getColorRGBA(Maybe<aiColor3D> &value) { |
388 | 674 | aiColor3D v; |
389 | 674 | getColorRGBA(&v); |
390 | 674 | value = Maybe<aiColor3D>(v); |
391 | 674 | } |
392 | | |
393 | | // ------------------------------------------------------------------- |
394 | | // Loads the kind of illumination model. |
395 | 1.70M | void ObjFileMtlImporter::getIlluminationModel(int &illum_model) { |
396 | 1.70M | m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE); |
397 | 1.70M | illum_model = atoi(&m_buffer[0]); |
398 | 1.70M | } |
399 | | |
400 | | |
401 | | // ------------------------------------------------------------------- |
402 | | // Loads a single float value. |
403 | 402k | void ObjFileMtlImporter::getFloatValue(ai_real &value) { |
404 | 402k | m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE); |
405 | 402k | size_t len = std::strlen(&m_buffer[0]); |
406 | 402k | if (0 == len) { |
407 | 45.7k | value = 0.0f; |
408 | 45.7k | return; |
409 | 45.7k | } |
410 | | |
411 | 356k | value = (ai_real)fast_atof(&m_buffer[0]); |
412 | 356k | } |
413 | | |
414 | | // ------------------------------------------------------------------- |
415 | 446k | void ObjFileMtlImporter::getFloatValue(Maybe<ai_real> &value) { |
416 | 446k | m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE); |
417 | 446k | size_t len = std::strlen(&m_buffer[0]); |
418 | 446k | if (len) |
419 | 6.56k | value = Maybe<ai_real>(fast_atof(&m_buffer[0])); |
420 | 439k | else |
421 | 439k | value = Maybe<ai_real>(); |
422 | 446k | } |
423 | | |
424 | | // ------------------------------------------------------------------- |
425 | | // Writes a loaded single float value if material not null |
426 | 31.6k | void ObjFileMtlImporter::getFloatIfMaterialValid(ai_real ObjFile::Material::*member) { |
427 | 31.6k | if (m_pModel != nullptr && m_pModel->mCurrentMaterial != nullptr) { |
428 | | // This will call getFloatValue(ai_real&) |
429 | 31.2k | getFloatValue(m_pModel->mCurrentMaterial->*member); |
430 | 31.2k | } |
431 | 31.6k | } |
432 | | |
433 | | // ------------------------------------------------------------------- |
434 | 440k | void ObjFileMtlImporter::getFloatIfMaterialValid(Maybe<ai_real> ObjFile::Material::*member) { |
435 | | // It can directly access `m_pModel` because it's part of the class |
436 | 440k | if (m_pModel != nullptr && m_pModel->mCurrentMaterial != nullptr) { |
437 | 438k | getFloatValue(m_pModel->mCurrentMaterial->*member); |
438 | 438k | } |
439 | 440k | } |
440 | | |
441 | | // ------------------------------------------------------------------- |
442 | | // Creates a material from loaded data. |
443 | 495k | void ObjFileMtlImporter::createMaterial() { |
444 | 495k | std::string line; |
445 | 53.1M | while (!IsLineEnd(*m_DataIt)) { |
446 | 52.6M | line += *m_DataIt; |
447 | 52.6M | ++m_DataIt; |
448 | 52.6M | } |
449 | | |
450 | 495k | std::vector<std::string> token; |
451 | 495k | const unsigned int numToken = tokenize<std::string>(line, token, " \t"); |
452 | 495k | std::string name; |
453 | 495k | if (numToken == 1) { |
454 | 39.1k | name = AI_DEFAULT_MATERIAL_NAME; |
455 | 456k | } else { |
456 | | // skip newmtl and all following white spaces |
457 | 456k | std::size_t first_ws_pos = line.find_first_of(" \t"); |
458 | 456k | std::size_t first_non_ws_pos = line.find_first_not_of(" \t", first_ws_pos); |
459 | 456k | if (first_non_ws_pos != std::string::npos) { |
460 | 456k | name = line.substr(first_non_ws_pos); |
461 | 456k | } |
462 | 456k | } |
463 | | |
464 | 495k | name = ai_trim(name); |
465 | | |
466 | 495k | std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->mMaterialMap.find(name); |
467 | 495k | if (m_pModel->mMaterialMap.end() == it) { |
468 | | // New Material created |
469 | 2.91k | m_pModel->mCurrentMaterial = new ObjFile::Material(); |
470 | 2.91k | m_pModel->mCurrentMaterial->MaterialName.Set(name); |
471 | 2.91k | m_pModel->mMaterialLib.push_back(name); |
472 | 2.91k | m_pModel->mMaterialMap[name] = m_pModel->mCurrentMaterial; |
473 | | |
474 | 2.91k | if (m_pModel->mCurrentMesh) { |
475 | 1.25k | m_pModel->mCurrentMesh->m_uiMaterialIndex = static_cast<unsigned int>(m_pModel->mMaterialLib.size() - 1); |
476 | 1.25k | } |
477 | 492k | } else { |
478 | | // Use older material |
479 | 492k | m_pModel->mCurrentMaterial = it->second; |
480 | 492k | } |
481 | 495k | } |
482 | | |
483 | | // ------------------------------------------------------------------- |
484 | | // Gets a texture name from data. |
485 | 831k | void ObjFileMtlImporter::getTexture() { |
486 | 831k | aiString *out = nullptr; |
487 | 831k | int clampIndex = -1; |
488 | | |
489 | 831k | if (m_pModel->mCurrentMaterial == nullptr) { |
490 | 1.44k | m_pModel->mCurrentMaterial = new ObjFile::Material(); |
491 | 1.44k | m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material"); |
492 | 1.44k | m_pModel->mMaterialMap["Empty_Material"] = m_pModel->mCurrentMaterial; |
493 | 1.44k | } |
494 | | |
495 | 831k | const char *pPtr(&(*m_DataIt)); |
496 | 831k | if (!ASSIMP_strincmp(pPtr, DiffuseTexture, static_cast<unsigned int>(strlen(DiffuseTexture)))) { |
497 | | // Diffuse texture |
498 | 147k | out = &m_pModel->mCurrentMaterial->texture; |
499 | 147k | clampIndex = ObjFile::Material::TextureDiffuseType; |
500 | 683k | } else if (!ASSIMP_strincmp(pPtr, AmbientTexture, static_cast<unsigned int>(strlen(AmbientTexture)))) { |
501 | | // Ambient texture |
502 | 5.48k | out = &m_pModel->mCurrentMaterial->textureAmbient; |
503 | 5.48k | clampIndex = ObjFile::Material::TextureAmbientType; |
504 | 678k | } else if (!ASSIMP_strincmp(pPtr, SpecularTexture, static_cast<unsigned int>(strlen(SpecularTexture)))) { |
505 | | // Specular texture |
506 | 12.6k | out = &m_pModel->mCurrentMaterial->textureSpecular; |
507 | 12.6k | clampIndex = ObjFile::Material::TextureSpecularType; |
508 | 665k | } else if (!ASSIMP_strincmp(pPtr, DisplacementTexture1, static_cast<unsigned int>(strlen(DisplacementTexture1))) || |
509 | 664k | !ASSIMP_strincmp(pPtr, DisplacementTexture2, static_cast<unsigned int>(strlen(DisplacementTexture2)))) { |
510 | | // Displacement texture |
511 | 46.2k | out = &m_pModel->mCurrentMaterial->textureDisp; |
512 | 46.2k | clampIndex = ObjFile::Material::TextureDispType; |
513 | 619k | } else if (!ASSIMP_strincmp(pPtr, OpacityTexture, static_cast<unsigned int>(strlen(OpacityTexture)))) { |
514 | | // Opacity texture |
515 | 2.03k | out = &m_pModel->mCurrentMaterial->textureOpacity; |
516 | 2.03k | clampIndex = ObjFile::Material::TextureOpacityType; |
517 | 617k | } else if (!ASSIMP_strincmp(pPtr, EmissiveTexture1, static_cast<unsigned int>(strlen(EmissiveTexture1))) || |
518 | 612k | !ASSIMP_strincmp(pPtr, EmissiveTexture2, static_cast<unsigned int>(strlen(EmissiveTexture2)))) { |
519 | | // Emissive texture |
520 | 49.2k | out = &m_pModel->mCurrentMaterial->textureEmissive; |
521 | 49.2k | clampIndex = ObjFile::Material::TextureEmissiveType; |
522 | 568k | } else if (!ASSIMP_strincmp(pPtr, BumpTexture1, static_cast<unsigned int>(strlen(BumpTexture1))) || |
523 | 567k | !ASSIMP_strincmp(pPtr, BumpTexture2, static_cast<unsigned int>(strlen(BumpTexture2)))) { |
524 | | // Bump texture |
525 | 4.68k | out = &m_pModel->mCurrentMaterial->textureBump; |
526 | 4.68k | clampIndex = ObjFile::Material::TextureBumpType; |
527 | 563k | } else if (!ASSIMP_strincmp(pPtr, NormalTextureV1, static_cast<unsigned int>(strlen(NormalTextureV1))) || !ASSIMP_strincmp(pPtr, NormalTextureV2, static_cast<unsigned int>(strlen(NormalTextureV2)))) { |
528 | | // Normal map |
529 | 86.5k | out = &m_pModel->mCurrentMaterial->textureNormal; |
530 | 86.5k | clampIndex = ObjFile::Material::TextureNormalType; |
531 | 477k | } else if (!ASSIMP_strincmp(pPtr, ReflectionTexture, static_cast<unsigned int>(strlen(ReflectionTexture)))) { |
532 | | // Reflection texture(s) |
533 | | //Do nothing here |
534 | 819 | return; |
535 | 476k | } else if (!ASSIMP_strincmp(pPtr, SpecularityTexture, static_cast<unsigned int>(strlen(SpecularityTexture)))) { |
536 | | // Specularity scaling (glossiness) |
537 | 2.52k | out = &m_pModel->mCurrentMaterial->textureSpecularity; |
538 | 2.52k | clampIndex = ObjFile::Material::TextureSpecularityType; |
539 | 473k | } else if ( !ASSIMP_strincmp( pPtr, RoughnessTexture, static_cast<unsigned int>(strlen(RoughnessTexture)))) { |
540 | | // PBR Roughness texture |
541 | 21.8k | out = & m_pModel->mCurrentMaterial->textureRoughness; |
542 | 21.8k | clampIndex = ObjFile::Material::TextureRoughnessType; |
543 | 451k | } else if ( !ASSIMP_strincmp( pPtr, MetallicTexture, static_cast<unsigned int>(strlen(MetallicTexture)))) { |
544 | | // PBR Metallic texture |
545 | 38.8k | out = & m_pModel->mCurrentMaterial->textureMetallic; |
546 | 38.8k | clampIndex = ObjFile::Material::TextureMetallicType; |
547 | 412k | } else if (!ASSIMP_strincmp( pPtr, SheenTexture, static_cast<unsigned int>(strlen(SheenTexture)))) { |
548 | | // PBR Sheen (reflectance) texture |
549 | 27.9k | out = & m_pModel->mCurrentMaterial->textureSheen; |
550 | 27.9k | clampIndex = ObjFile::Material::TextureSheenType; |
551 | 385k | } else if (!ASSIMP_strincmp( pPtr, RMATexture, static_cast<unsigned int>(strlen(RMATexture)))) { |
552 | | // PBR Rough/Metal/AO texture |
553 | 0 | out = & m_pModel->mCurrentMaterial->textureRMA; |
554 | 0 | clampIndex = ObjFile::Material::TextureRMAType; |
555 | 385k | } else { |
556 | 385k | ASSIMP_LOG_ERROR("OBJ/MTL: Encountered unknown texture type"); |
557 | 385k | return; |
558 | 385k | } |
559 | | |
560 | 445k | bool clamp = false; |
561 | 445k | getTextureOption(clamp, clampIndex, out); |
562 | 445k | m_pModel->mCurrentMaterial->clamp[clampIndex] = clamp; |
563 | | |
564 | 445k | std::string texture; |
565 | 445k | m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, texture); |
566 | 445k | if (nullptr != out) { |
567 | 445k | out->Set(m_strAbsPath + texture); |
568 | 445k | } |
569 | 445k | } |
570 | | |
571 | | /* ///////////////////////////////////////////////////////////////////////////// |
572 | | * Texture Option |
573 | | * ///////////////////////////////////////////////////////////////////////////// |
574 | | * According to http://en.wikipedia.org/wiki/Wavefront_.obj_file#Texture_options |
575 | | * Texture map statement can contains various texture option, for example: |
576 | | * |
577 | | * map_Ka -o 1 1 1 some.png |
578 | | * map_Kd -clamp on some.png |
579 | | * |
580 | | * So we need to parse and skip these options, and leave the last part which is |
581 | | * the url of image, otherwise we will get a wrong url like "-clamp on some.png". |
582 | | * |
583 | | * Because aiMaterial supports clamp option, so we also want to return it |
584 | | * ///////////////////////////////////////////////////////////////////////////// |
585 | | */ |
586 | 445k | void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString *&out) { |
587 | 445k | m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
588 | | |
589 | | // If there is any more texture option |
590 | 510k | while (!isEndOfBuffer(m_DataIt, m_DataItEnd) && *m_DataIt == '-') { |
591 | 64.9k | const char *pPtr(&(*m_DataIt)); |
592 | | //skip option key and value |
593 | 64.9k | int skipToken = 1; |
594 | | |
595 | 64.9k | if (!ASSIMP_strincmp(pPtr, ClampOption, static_cast<unsigned int>(strlen(ClampOption)))) { |
596 | 1.36k | DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
597 | 1.36k | char value[3]; |
598 | 1.36k | CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value)); |
599 | 1.36k | if (!ASSIMP_strincmp(value, "on", 2)) { |
600 | 0 | clamp = true; |
601 | 0 | } |
602 | | |
603 | 1.36k | skipToken = 2; |
604 | 63.6k | } else if (!ASSIMP_strincmp(pPtr, TypeOption, static_cast<unsigned int>(strlen(TypeOption)))) { |
605 | 29.4k | DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
606 | 29.4k | char value[12]; |
607 | 29.4k | CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value)); |
608 | 29.4k | if (!ASSIMP_strincmp(value, "cube_top", 8)) { |
609 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeTopType; |
610 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[0]; |
611 | 29.4k | } else if (!ASSIMP_strincmp(value, "cube_bottom", 11)) { |
612 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeBottomType; |
613 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[1]; |
614 | 29.4k | } else if (!ASSIMP_strincmp(value, "cube_front", 10)) { |
615 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeFrontType; |
616 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[2]; |
617 | 29.4k | } else if (!ASSIMP_strincmp(value, "cube_back", 9)) { |
618 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeBackType; |
619 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[3]; |
620 | 29.4k | } else if (!ASSIMP_strincmp(value, "cube_left", 9)) { |
621 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeLeftType; |
622 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[4]; |
623 | 29.4k | } else if (!ASSIMP_strincmp(value, "cube_right", 10)) { |
624 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeRightType; |
625 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[5]; |
626 | 29.4k | } else if (!ASSIMP_strincmp(value, "sphere", 6)) { |
627 | 0 | clampIndex = ObjFile::Material::TextureReflectionSphereType; |
628 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[0]; |
629 | 0 | } |
630 | | |
631 | 29.4k | skipToken = 2; |
632 | 34.1k | } else if (!ASSIMP_strincmp(pPtr, BumpOption, static_cast<unsigned int>(strlen(BumpOption)))) { |
633 | 220 | DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
634 | 220 | getFloat(it, m_DataItEnd, m_pModel->mCurrentMaterial->bump_multiplier); |
635 | 220 | skipToken = 2; |
636 | 33.9k | } else if (!ASSIMP_strincmp(pPtr, BlendUOption, static_cast<unsigned int>(strlen(BlendUOption))) || |
637 | 33.9k | !ASSIMP_strincmp(pPtr, BlendVOption, static_cast<unsigned int>(strlen(BlendVOption))) || |
638 | 33.7k | !ASSIMP_strincmp(pPtr, BoostOption, static_cast<unsigned int>(strlen(BoostOption))) || |
639 | 33.5k | !ASSIMP_strincmp(pPtr, ResolutionOption, static_cast<unsigned int>(strlen(ResolutionOption))) || |
640 | 33.1k | !ASSIMP_strincmp(pPtr, ChannelOption, static_cast<unsigned int>(strlen(ChannelOption)))) { |
641 | 5.24k | skipToken = 2; |
642 | 28.6k | } else if (!ASSIMP_strincmp(pPtr, ModifyMapOption, static_cast<unsigned int>(strlen(ModifyMapOption)))) { |
643 | 64 | skipToken = 3; |
644 | 28.6k | } else if (!ASSIMP_strincmp(pPtr, OffsetOption, static_cast<unsigned int>(strlen(OffsetOption))) || |
645 | 28.5k | !ASSIMP_strincmp(pPtr, ScaleOption, static_cast<unsigned int>(strlen(ScaleOption))) || |
646 | 28.1k | !ASSIMP_strincmp(pPtr, TurbulenceOption, static_cast<unsigned int>(strlen(TurbulenceOption)))) { |
647 | 1.50k | skipToken = 4; |
648 | 1.50k | } |
649 | | |
650 | 170k | for (int i = 0; i < skipToken; ++i) { |
651 | 105k | m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
652 | 105k | } |
653 | 64.9k | } |
654 | 445k | } |
655 | | |
656 | | // ------------------------------------------------------------------- |
657 | | |
658 | | } // Namespace Assimp |
659 | | |
660 | | #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER |