/src/assimp/code/AssetLib/Obj/ObjFileMtlImporter.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2025, 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 | 4.90k | m_strAbsPath(strAbsPath), |
96 | 4.90k | m_DataIt(buffer.begin()), |
97 | 4.90k | m_DataItEnd(buffer.end()), |
98 | 4.90k | m_pModel(pModel), |
99 | 4.90k | m_uiLine(0), |
100 | 4.90k | m_buffer() { |
101 | 4.90k | ai_assert(nullptr != m_pModel); |
102 | 4.90k | m_buffer.resize(BUFFERSIZE); |
103 | 4.90k | std::fill(m_buffer.begin(), m_buffer.end(), '\0'); |
104 | 4.90k | 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 | 4.90k | char folderSeparator = DefaultIOSystem().getOsSeparator(); |
111 | 4.90k | std::size_t found = m_strAbsPath.find_last_of(folderSeparator); |
112 | 4.90k | if (found == std::string::npos) { |
113 | | // Not found, try alternative folder separator |
114 | 4.89k | folderSeparator = (folderSeparator == '/' ? '\\' : '/'); |
115 | 4.89k | found = m_strAbsPath.find_last_of(folderSeparator); |
116 | 4.89k | } |
117 | 4.90k | if (found != std::string::npos) { |
118 | 30 | m_strAbsPath = m_strAbsPath.substr(0, found + 1); |
119 | 4.87k | } else { |
120 | 4.87k | m_strAbsPath = ""; |
121 | 4.87k | } |
122 | 4.90k | load(); |
123 | 4.90k | } |
124 | | |
125 | | // ------------------------------------------------------------------- |
126 | | // Loads the material description |
127 | 4.90k | void ObjFileMtlImporter::load() { |
128 | 4.90k | if (m_DataIt == m_DataItEnd) |
129 | 0 | return; |
130 | | |
131 | 37.3M | while (m_DataIt != m_DataItEnd) { |
132 | 37.3M | switch (*m_DataIt) { |
133 | 1.29k | case 'k': |
134 | 22.4k | case 'K': { |
135 | 22.4k | ++m_DataIt; |
136 | 22.4k | if (*m_DataIt == 'a') // Ambient color |
137 | 559 | { |
138 | 559 | ++m_DataIt; |
139 | 559 | if (m_pModel->mCurrentMaterial != nullptr) |
140 | 559 | getColorRGBA(&m_pModel->mCurrentMaterial->ambient); |
141 | 21.8k | } else if (*m_DataIt == 'd') { |
142 | | // Diffuse color |
143 | 32 | ++m_DataIt; |
144 | 32 | if (m_pModel->mCurrentMaterial != nullptr) |
145 | 32 | getColorRGBA(&m_pModel->mCurrentMaterial->diffuse); |
146 | 21.8k | } else if (*m_DataIt == 's') { |
147 | 0 | ++m_DataIt; |
148 | 0 | if (m_pModel->mCurrentMaterial != nullptr) |
149 | 0 | getColorRGBA(&m_pModel->mCurrentMaterial->specular); |
150 | 21.8k | } else if (*m_DataIt == 'e') { |
151 | 400 | ++m_DataIt; |
152 | 400 | if (m_pModel->mCurrentMaterial != nullptr) |
153 | 400 | getColorRGBA(&m_pModel->mCurrentMaterial->emissive); |
154 | 400 | } |
155 | 22.4k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
156 | 22.4k | } break; |
157 | 6.11k | case 'T': { |
158 | 6.11k | ++m_DataIt; |
159 | | // Material transmission color |
160 | 6.11k | if (*m_DataIt == 'f') { |
161 | 53 | ++m_DataIt; |
162 | 53 | if (m_pModel->mCurrentMaterial != nullptr) |
163 | 53 | getColorRGBA(&m_pModel->mCurrentMaterial->transparent); |
164 | 6.06k | } else if (*m_DataIt == 'r') { |
165 | | // Material transmission alpha value |
166 | 899 | ++m_DataIt; |
167 | 899 | ai_real d; |
168 | 899 | getFloatValue(d); |
169 | 899 | if (m_pModel->mCurrentMaterial != nullptr) |
170 | 899 | m_pModel->mCurrentMaterial->alpha = static_cast<ai_real>(1.0) - d; |
171 | 899 | } |
172 | 6.11k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
173 | 6.11k | } break; |
174 | 1.25M | case 'd': { |
175 | 1.25M | if (*(m_DataIt + 1) == 'i' && *(m_DataIt + 2) == 's' && *(m_DataIt + 3) == 'p') { |
176 | | // A displacement map |
177 | 36 | getTexture(); |
178 | 1.25M | } else { |
179 | | // Alpha value |
180 | 1.25M | ++m_DataIt; |
181 | 1.25M | if (m_pModel->mCurrentMaterial != nullptr) |
182 | 35.5k | getFloatValue(m_pModel->mCurrentMaterial->alpha); |
183 | 1.25M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
184 | 1.25M | } |
185 | 1.25M | } break; |
186 | | |
187 | 5.14k | case 'N': |
188 | 1.22M | case 'n': { |
189 | 1.22M | ++m_DataIt; |
190 | 1.22M | switch (*m_DataIt) { |
191 | 382 | case 's': // Specular exponent |
192 | 382 | ++m_DataIt; |
193 | 382 | if (m_pModel->mCurrentMaterial != nullptr) |
194 | 382 | getFloatValue(m_pModel->mCurrentMaterial->shineness); |
195 | 382 | break; |
196 | 61.4k | case 'i': // Index Of refraction |
197 | 61.4k | ++m_DataIt; |
198 | 61.4k | if (m_pModel->mCurrentMaterial != nullptr) |
199 | 61.4k | getFloatValue(m_pModel->mCurrentMaterial->ior); |
200 | 61.4k | break; |
201 | 964k | case 'e': // New material |
202 | 964k | createMaterial(); |
203 | 964k | break; |
204 | 6.28k | case 'o': // Norm texture |
205 | 6.28k | --m_DataIt; |
206 | 6.28k | getTexture(); |
207 | 6.28k | break; |
208 | 1.22M | } |
209 | 1.22M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
210 | 1.22M | } break; |
211 | | |
212 | 29.8k | case 'P': |
213 | 29.8k | { |
214 | 29.8k | ++m_DataIt; |
215 | 29.8k | switch(*m_DataIt) |
216 | 29.8k | { |
217 | 2.94k | case 'r': |
218 | 2.94k | ++m_DataIt; |
219 | 2.94k | if (m_pModel->mCurrentMaterial != nullptr) |
220 | 2.94k | getFloatValue(m_pModel->mCurrentMaterial->roughness); |
221 | 2.94k | break; |
222 | 0 | case 'm': |
223 | 0 | ++m_DataIt; |
224 | 0 | if (m_pModel->mCurrentMaterial != nullptr) |
225 | 0 | getFloatValue(m_pModel->mCurrentMaterial->metallic); |
226 | 0 | break; |
227 | 0 | case 's': |
228 | 0 | ++m_DataIt; |
229 | 0 | if (m_pModel->mCurrentMaterial != nullptr) |
230 | 0 | getColorRGBA(m_pModel->mCurrentMaterial->sheen); |
231 | 0 | break; |
232 | 11.7k | case 'c': |
233 | 11.7k | ++m_DataIt; |
234 | 11.7k | if (*m_DataIt == 'r') { |
235 | 3.78k | ++m_DataIt; |
236 | 3.78k | if (m_pModel->mCurrentMaterial != nullptr) |
237 | 3.78k | getFloatValue(m_pModel->mCurrentMaterial->clearcoat_roughness); |
238 | 7.92k | } else if (*m_DataIt == 't') { |
239 | 58 | ++m_DataIt; |
240 | 58 | if (m_pModel->mCurrentMaterial != nullptr) |
241 | 58 | getFloatValue(m_pModel->mCurrentMaterial->clearcoat_thickness); |
242 | 7.86k | } else { |
243 | 7.86k | if (m_pModel->mCurrentMaterial != nullptr) |
244 | 7.86k | getFloatValue(m_pModel->mCurrentMaterial->clearcoat); |
245 | 7.86k | } |
246 | 11.7k | break; |
247 | 29.8k | } |
248 | 29.8k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
249 | 29.8k | } |
250 | 0 | break; |
251 | | |
252 | 5.74M | case 'm': // Texture or metallic |
253 | 5.74M | { |
254 | | // Save start of token (after 'm') |
255 | 5.74M | auto tokenStart = m_DataIt; // points to 'm' |
256 | 5.74M | auto tokenEnd = getNextToken(m_DataIt, m_DataItEnd); // move iterator to end of token |
257 | | |
258 | 5.74M | std::string keyword(tokenStart, tokenEnd); |
259 | 5.74M | m_DataIt = tokenEnd; // advance iterator |
260 | | |
261 | 5.74M | if (keyword.compare(0, 3, "map") == 0) { |
262 | | // starts with "map", treat as texture map |
263 | 1.56M | m_DataIt = tokenStart; |
264 | 1.56M | getTexture(); |
265 | 4.17M | } else if (keyword == "metallic" || keyword == "metal" || keyword == "metalness") { |
266 | | // parse metallic float value instead of texture |
267 | 359k | getFloatIfMaterialValid(&ObjFile::Material::metallic); |
268 | 359k | } |
269 | | |
270 | 5.74M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
271 | 5.74M | } break; |
272 | | |
273 | 6.81k | case 'b': // quick'n'dirty - for 'bump' sections |
274 | 6.81k | { |
275 | 6.81k | getTexture(); |
276 | 6.81k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
277 | 6.81k | } break; |
278 | | |
279 | 51.9k | case 'r': // refl (map) or roughness (float) |
280 | 51.9k | { |
281 | 51.9k | auto tokenStart = m_DataIt; // points to 'r' |
282 | 51.9k | auto tokenEnd = getNextToken(m_DataIt, m_DataItEnd); |
283 | 51.9k | std::string keyword(tokenStart, tokenEnd); |
284 | 51.9k | m_DataIt = tokenEnd; |
285 | | |
286 | 51.9k | if (keyword == "roughness" || keyword == "rough") { |
287 | 0 | getFloatIfMaterialValid(&ObjFile::Material::roughness); |
288 | 51.9k | } else if (keyword == "refl" || keyword == "reflection") { |
289 | 49 | m_DataIt = tokenStart; |
290 | 49 | getTexture(); |
291 | 49 | } |
292 | | |
293 | 51.9k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
294 | 51.9k | } break; |
295 | | |
296 | 313k | case 'i': // Illumination model |
297 | 313k | { |
298 | 313k | m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
299 | 313k | if (m_pModel->mCurrentMaterial != nullptr) |
300 | 80.1k | getIlluminationModel(m_pModel->mCurrentMaterial->illumination_model); |
301 | 313k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
302 | 313k | } break; |
303 | | |
304 | 22.4k | case 'a': { |
305 | 22.4k | auto tokenStart = m_DataIt; |
306 | 22.4k | auto tokenEnd = getNextToken(m_DataIt, m_DataItEnd); |
307 | 22.4k | std::string keyword(tokenStart, tokenEnd); |
308 | 22.4k | m_DataIt = tokenEnd; |
309 | | |
310 | 22.4k | if (keyword == "aniso" || keyword == "anisotropy") { |
311 | 0 | getFloatIfMaterialValid(&ObjFile::Material::anisotropy); |
312 | 22.4k | } else if (keyword == "ao") { |
313 | 0 | getFloatIfMaterialValid(&ObjFile::Material::ambient_occlusion); |
314 | 22.4k | } else if (keyword == "anisor" || ai_stdStrToLower(keyword) == "anisotropicrotation") { |
315 | 0 | getFloatIfMaterialValid(&ObjFile::Material::anisotropy_rotation); |
316 | 22.4k | } else { |
317 | 22.4k | ASSIMP_LOG_WARN("Unhandled keyword: ", keyword ); |
318 | 22.4k | } |
319 | | |
320 | 22.4k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
321 | 22.4k | } break; |
322 | | |
323 | 165k | case 's': { |
324 | 165k | auto tokenStart = m_DataIt; |
325 | 165k | auto tokenEnd = getNextToken(m_DataIt, m_DataItEnd); |
326 | 165k | std::string keyword(tokenStart, tokenEnd); |
327 | 165k | m_DataIt = tokenEnd; |
328 | | |
329 | 165k | if (keyword == "subsurface" || keyword == "scattering") { |
330 | 52 | getFloatIfMaterialValid(&ObjFile::Material::subsurface_scattering); |
331 | 165k | } else if (ai_stdStrToLower(keyword) == "speculartint") { |
332 | 0 | getFloatIfMaterialValid(&ObjFile::Material::specular_tint); |
333 | 165k | } else if (keyword == "sheen") { |
334 | 0 | getFloatIfMaterialValid(&ObjFile::Material::sheen_grazing); |
335 | 165k | } else if (ai_stdStrToLower(keyword) == "sheentint") { |
336 | 0 | getFloatIfMaterialValid(&ObjFile::Material::sheen_tint); |
337 | 165k | } else { |
338 | 165k | ASSIMP_LOG_WARN("Unhandled keyword: ", keyword ); |
339 | 165k | } |
340 | | |
341 | 165k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
342 | 165k | } break; |
343 | | |
344 | 25.5k | case 'c': { |
345 | 25.5k | auto tokenStart = m_DataIt; |
346 | 25.5k | auto tokenEnd = getNextToken(m_DataIt, m_DataItEnd); |
347 | 25.5k | std::string keyword(tokenStart, tokenEnd); |
348 | 25.5k | m_DataIt = tokenEnd; |
349 | | |
350 | 25.5k | if (ai_stdStrToLower(keyword) == "clearcoat") { |
351 | 0 | getFloatIfMaterialValid(&ObjFile::Material::clearcoat); |
352 | 25.5k | } else if (ai_stdStrToLower(keyword) == "clearcoatgloss") { |
353 | 0 | getFloatIfMaterialValid(&ObjFile::Material::clearcoat_gloss); |
354 | 25.5k | } else { |
355 | 25.5k | ASSIMP_LOG_WARN("Unhandled keyword: ", keyword ); |
356 | 25.5k | } |
357 | | |
358 | 25.5k | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
359 | 25.5k | } break; |
360 | | |
361 | 28.5M | default: { |
362 | 28.5M | m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); |
363 | 28.5M | } break; |
364 | 37.3M | } |
365 | 37.3M | } |
366 | 4.90k | } |
367 | | |
368 | | // ------------------------------------------------------------------- |
369 | | // Loads a color definition |
370 | 1.04k | void ObjFileMtlImporter::getColorRGBA(aiColor3D *pColor) { |
371 | 1.04k | ai_assert(nullptr != pColor); |
372 | | |
373 | 1.04k | ai_real r(0.0), g(0.0), b(0.0); |
374 | 1.04k | m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, r); |
375 | 1.04k | pColor->r = r; |
376 | | |
377 | | // we have to check if color is default 0 with only one token |
378 | 1.04k | if (!IsLineEnd(*m_DataIt)) { |
379 | 214 | m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, g); |
380 | 214 | m_DataIt = getFloat<DataArrayIt>(m_DataIt, m_DataItEnd, b); |
381 | 214 | } |
382 | 1.04k | pColor->g = g; |
383 | 1.04k | pColor->b = b; |
384 | 1.04k | } |
385 | | |
386 | | // ------------------------------------------------------------------- |
387 | 0 | void ObjFileMtlImporter::getColorRGBA(Maybe<aiColor3D> &value) { |
388 | 0 | aiColor3D v; |
389 | 0 | getColorRGBA(&v); |
390 | 0 | value = Maybe<aiColor3D>(v); |
391 | 0 | } |
392 | | |
393 | | // ------------------------------------------------------------------- |
394 | | // Loads the kind of illumination model. |
395 | 80.1k | void ObjFileMtlImporter::getIlluminationModel(int &illum_model) { |
396 | 80.1k | m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE); |
397 | 80.1k | illum_model = atoi(&m_buffer[0]); |
398 | 80.1k | } |
399 | | |
400 | | |
401 | | // ------------------------------------------------------------------- |
402 | | // Loads a single float value. |
403 | 98.2k | void ObjFileMtlImporter::getFloatValue(ai_real &value) { |
404 | 98.2k | m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE); |
405 | 98.2k | size_t len = std::strlen(&m_buffer[0]); |
406 | 98.2k | if (0 == len) { |
407 | 77.8k | value = 0.0f; |
408 | 77.8k | return; |
409 | 77.8k | } |
410 | | |
411 | 20.4k | value = (ai_real)fast_atof(&m_buffer[0]); |
412 | 20.4k | } |
413 | | |
414 | | // ------------------------------------------------------------------- |
415 | 373k | void ObjFileMtlImporter::getFloatValue(Maybe<ai_real> &value) { |
416 | 373k | m_DataIt = CopyNextWord<DataArrayIt>(m_DataIt, m_DataItEnd, &m_buffer[0], BUFFERSIZE); |
417 | 373k | size_t len = std::strlen(&m_buffer[0]); |
418 | 373k | if (len) |
419 | 11.5k | value = Maybe<ai_real>(fast_atof(&m_buffer[0])); |
420 | 362k | else |
421 | 362k | value = Maybe<ai_real>(); |
422 | 373k | } |
423 | | |
424 | | // ------------------------------------------------------------------- |
425 | | // Writes a loaded single float value if material not null |
426 | 0 | void ObjFileMtlImporter::getFloatIfMaterialValid(ai_real ObjFile::Material::*member) { |
427 | 0 | if (m_pModel != nullptr && m_pModel->mCurrentMaterial != nullptr) { |
428 | | // This will call getFloatValue(ai_real&) |
429 | 0 | getFloatValue(m_pModel->mCurrentMaterial->*member); |
430 | 0 | } |
431 | 0 | } |
432 | | |
433 | | // ------------------------------------------------------------------- |
434 | 359k | void ObjFileMtlImporter::getFloatIfMaterialValid(Maybe<ai_real> ObjFile::Material::*member) { |
435 | | // It can directly access `m_pModel` because it's part of the class |
436 | 359k | if (m_pModel != nullptr && m_pModel->mCurrentMaterial != nullptr) { |
437 | 359k | getFloatValue(m_pModel->mCurrentMaterial->*member); |
438 | 359k | } |
439 | 359k | } |
440 | | |
441 | | // ------------------------------------------------------------------- |
442 | | // Creates a material from loaded data. |
443 | 964k | void ObjFileMtlImporter::createMaterial() { |
444 | 964k | std::string line; |
445 | 36.0M | while (!IsLineEnd(*m_DataIt)) { |
446 | 35.0M | line += *m_DataIt; |
447 | 35.0M | ++m_DataIt; |
448 | 35.0M | } |
449 | | |
450 | 964k | std::vector<std::string> token; |
451 | 964k | const unsigned int numToken = tokenize<std::string>(line, token, " \t"); |
452 | 964k | std::string name; |
453 | 964k | if (numToken == 1) { |
454 | 700k | name = AI_DEFAULT_MATERIAL_NAME; |
455 | 700k | } else { |
456 | | // skip newmtl and all following white spaces |
457 | 263k | std::size_t first_ws_pos = line.find_first_of(" \t"); |
458 | 263k | std::size_t first_non_ws_pos = line.find_first_not_of(" \t", first_ws_pos); |
459 | 263k | if (first_non_ws_pos != std::string::npos) { |
460 | 263k | name = line.substr(first_non_ws_pos); |
461 | 263k | } |
462 | 263k | } |
463 | | |
464 | 964k | name = ai_trim(name); |
465 | | |
466 | 964k | std::map<std::string, ObjFile::Material *>::iterator it = m_pModel->mMaterialMap.find(name); |
467 | 964k | if (m_pModel->mMaterialMap.end() == it) { |
468 | | // New Material created |
469 | 142 | m_pModel->mCurrentMaterial = new ObjFile::Material(); |
470 | 142 | m_pModel->mCurrentMaterial->MaterialName.Set(name); |
471 | 142 | m_pModel->mMaterialLib.push_back(name); |
472 | 142 | m_pModel->mMaterialMap[name] = m_pModel->mCurrentMaterial; |
473 | | |
474 | 142 | if (m_pModel->mCurrentMesh) { |
475 | 40 | m_pModel->mCurrentMesh->m_uiMaterialIndex = static_cast<unsigned int>(m_pModel->mMaterialLib.size() - 1); |
476 | 40 | } |
477 | 964k | } else { |
478 | | // Use older material |
479 | 964k | m_pModel->mCurrentMaterial = it->second; |
480 | 964k | } |
481 | 964k | } |
482 | | |
483 | | // ------------------------------------------------------------------- |
484 | | // Gets a texture name from data. |
485 | 1.57M | void ObjFileMtlImporter::getTexture() { |
486 | 1.57M | aiString *out = nullptr; |
487 | 1.57M | int clampIndex = -1; |
488 | | |
489 | 1.57M | if (m_pModel->mCurrentMaterial == nullptr) { |
490 | 111 | m_pModel->mCurrentMaterial = new ObjFile::Material(); |
491 | 111 | m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material"); |
492 | 111 | m_pModel->mMaterialMap["Empty_Material"] = m_pModel->mCurrentMaterial; |
493 | 111 | } |
494 | | |
495 | 1.57M | const char *pPtr(&(*m_DataIt)); |
496 | 1.57M | if (!ASSIMP_strincmp(pPtr, DiffuseTexture, static_cast<unsigned int>(strlen(DiffuseTexture)))) { |
497 | | // Diffuse texture |
498 | 2.01k | out = &m_pModel->mCurrentMaterial->texture; |
499 | 2.01k | clampIndex = ObjFile::Material::TextureDiffuseType; |
500 | 1.57M | } else if (!ASSIMP_strincmp(pPtr, AmbientTexture, static_cast<unsigned int>(strlen(AmbientTexture)))) { |
501 | | // Ambient texture |
502 | 2.44k | out = &m_pModel->mCurrentMaterial->textureAmbient; |
503 | 2.44k | clampIndex = ObjFile::Material::TextureAmbientType; |
504 | 1.57M | } else if (!ASSIMP_strincmp(pPtr, SpecularTexture, static_cast<unsigned int>(strlen(SpecularTexture)))) { |
505 | | // Specular texture |
506 | 632k | out = &m_pModel->mCurrentMaterial->textureSpecular; |
507 | 632k | clampIndex = ObjFile::Material::TextureSpecularType; |
508 | 942k | } else if (!ASSIMP_strincmp(pPtr, DisplacementTexture1, static_cast<unsigned int>(strlen(DisplacementTexture1))) || |
509 | 942k | !ASSIMP_strincmp(pPtr, DisplacementTexture2, static_cast<unsigned int>(strlen(DisplacementTexture2)))) { |
510 | | // Displacement texture |
511 | 36 | out = &m_pModel->mCurrentMaterial->textureDisp; |
512 | 36 | clampIndex = ObjFile::Material::TextureDispType; |
513 | 942k | } else if (!ASSIMP_strincmp(pPtr, OpacityTexture, static_cast<unsigned int>(strlen(OpacityTexture)))) { |
514 | | // Opacity texture |
515 | 7.97k | out = &m_pModel->mCurrentMaterial->textureOpacity; |
516 | 7.97k | clampIndex = ObjFile::Material::TextureOpacityType; |
517 | 934k | } else if (!ASSIMP_strincmp(pPtr, EmissiveTexture1, static_cast<unsigned int>(strlen(EmissiveTexture1))) || |
518 | 934k | !ASSIMP_strincmp(pPtr, EmissiveTexture2, static_cast<unsigned int>(strlen(EmissiveTexture2)))) { |
519 | | // Emissive texture |
520 | 308 | out = &m_pModel->mCurrentMaterial->textureEmissive; |
521 | 308 | clampIndex = ObjFile::Material::TextureEmissiveType; |
522 | 934k | } else if (!ASSIMP_strincmp(pPtr, BumpTexture1, static_cast<unsigned int>(strlen(BumpTexture1))) || |
523 | 931k | !ASSIMP_strincmp(pPtr, BumpTexture2, static_cast<unsigned int>(strlen(BumpTexture2)))) { |
524 | | // Bump texture |
525 | 6.67k | out = &m_pModel->mCurrentMaterial->textureBump; |
526 | 6.67k | clampIndex = ObjFile::Material::TextureBumpType; |
527 | 927k | } 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 | 281k | out = &m_pModel->mCurrentMaterial->textureNormal; |
530 | 281k | clampIndex = ObjFile::Material::TextureNormalType; |
531 | 646k | } else if (!ASSIMP_strincmp(pPtr, ReflectionTexture, static_cast<unsigned int>(strlen(ReflectionTexture)))) { |
532 | | // Reflection texture(s) |
533 | | //Do nothing here |
534 | 49 | return; |
535 | 646k | } else if (!ASSIMP_strincmp(pPtr, SpecularityTexture, static_cast<unsigned int>(strlen(SpecularityTexture)))) { |
536 | | // Specularity scaling (glossiness) |
537 | 331 | out = &m_pModel->mCurrentMaterial->textureSpecularity; |
538 | 331 | clampIndex = ObjFile::Material::TextureSpecularityType; |
539 | 645k | } else if ( !ASSIMP_strincmp( pPtr, RoughnessTexture, static_cast<unsigned int>(strlen(RoughnessTexture)))) { |
540 | | // PBR Roughness texture |
541 | 3.96k | out = & m_pModel->mCurrentMaterial->textureRoughness; |
542 | 3.96k | clampIndex = ObjFile::Material::TextureRoughnessType; |
543 | 641k | } else if ( !ASSIMP_strincmp( pPtr, MetallicTexture, static_cast<unsigned int>(strlen(MetallicTexture)))) { |
544 | | // PBR Metallic texture |
545 | 369 | out = & m_pModel->mCurrentMaterial->textureMetallic; |
546 | 369 | clampIndex = ObjFile::Material::TextureMetallicType; |
547 | 641k | } else if (!ASSIMP_strincmp( pPtr, SheenTexture, static_cast<unsigned int>(strlen(SheenTexture)))) { |
548 | | // PBR Sheen (reflectance) texture |
549 | 72 | out = & m_pModel->mCurrentMaterial->textureSheen; |
550 | 72 | clampIndex = ObjFile::Material::TextureSheenType; |
551 | 641k | } 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 | 641k | } else { |
556 | 641k | ASSIMP_LOG_ERROR("OBJ/MTL: Encountered unknown texture type"); |
557 | 641k | return; |
558 | 641k | } |
559 | | |
560 | 937k | bool clamp = false; |
561 | 937k | getTextureOption(clamp, clampIndex, out); |
562 | 937k | m_pModel->mCurrentMaterial->clamp[clampIndex] = clamp; |
563 | | |
564 | 937k | std::string texture; |
565 | 937k | m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, texture); |
566 | 937k | if (nullptr != out) { |
567 | 937k | out->Set(m_strAbsPath + texture); |
568 | 937k | } |
569 | 937k | } |
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 | 937k | void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString *&out) { |
587 | 937k | m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
588 | | |
589 | | // If there is any more texture option |
590 | 1.67M | while (!isEndOfBuffer(m_DataIt, m_DataItEnd) && *m_DataIt == '-') { |
591 | 736k | const char *pPtr(&(*m_DataIt)); |
592 | | //skip option key and value |
593 | 736k | int skipToken = 1; |
594 | | |
595 | 736k | if (!ASSIMP_strincmp(pPtr, ClampOption, static_cast<unsigned int>(strlen(ClampOption)))) { |
596 | 579 | DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
597 | 579 | char value[3]; |
598 | 579 | CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value)); |
599 | 579 | if (!ASSIMP_strincmp(value, "on", 2)) { |
600 | 0 | clamp = true; |
601 | 0 | } |
602 | | |
603 | 579 | skipToken = 2; |
604 | 735k | } else if (!ASSIMP_strincmp(pPtr, TypeOption, static_cast<unsigned int>(strlen(TypeOption)))) { |
605 | 535k | DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
606 | 535k | char value[12]; |
607 | 535k | CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value)); |
608 | 535k | if (!ASSIMP_strincmp(value, "cube_top", 8)) { |
609 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeTopType; |
610 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[0]; |
611 | 535k | } else if (!ASSIMP_strincmp(value, "cube_bottom", 11)) { |
612 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeBottomType; |
613 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[1]; |
614 | 535k | } else if (!ASSIMP_strincmp(value, "cube_front", 10)) { |
615 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeFrontType; |
616 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[2]; |
617 | 535k | } else if (!ASSIMP_strincmp(value, "cube_back", 9)) { |
618 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeBackType; |
619 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[3]; |
620 | 535k | } else if (!ASSIMP_strincmp(value, "cube_left", 9)) { |
621 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeLeftType; |
622 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[4]; |
623 | 535k | } else if (!ASSIMP_strincmp(value, "cube_right", 10)) { |
624 | 0 | clampIndex = ObjFile::Material::TextureReflectionCubeRightType; |
625 | 0 | out = &m_pModel->mCurrentMaterial->textureReflection[5]; |
626 | 535k | } else if (!ASSIMP_strincmp(value, "sphere", 6)) { |
627 | 68 | clampIndex = ObjFile::Material::TextureReflectionSphereType; |
628 | 68 | out = &m_pModel->mCurrentMaterial->textureReflection[0]; |
629 | 68 | } |
630 | | |
631 | 535k | skipToken = 2; |
632 | 535k | } else if (!ASSIMP_strincmp(pPtr, BumpOption, static_cast<unsigned int>(strlen(BumpOption)))) { |
633 | 0 | DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
634 | 0 | getFloat(it, m_DataItEnd, m_pModel->mCurrentMaterial->bump_multiplier); |
635 | 0 | skipToken = 2; |
636 | 200k | } else if (!ASSIMP_strincmp(pPtr, BlendUOption, static_cast<unsigned int>(strlen(BlendUOption))) || |
637 | 200k | !ASSIMP_strincmp(pPtr, BlendVOption, static_cast<unsigned int>(strlen(BlendVOption))) || |
638 | 200k | !ASSIMP_strincmp(pPtr, BoostOption, static_cast<unsigned int>(strlen(BoostOption))) || |
639 | 200k | !ASSIMP_strincmp(pPtr, ResolutionOption, static_cast<unsigned int>(strlen(ResolutionOption))) || |
640 | 199k | !ASSIMP_strincmp(pPtr, ChannelOption, static_cast<unsigned int>(strlen(ChannelOption)))) { |
641 | 398 | skipToken = 2; |
642 | 199k | } else if (!ASSIMP_strincmp(pPtr, ModifyMapOption, static_cast<unsigned int>(strlen(ModifyMapOption)))) { |
643 | 0 | skipToken = 3; |
644 | 199k | } else if (!ASSIMP_strincmp(pPtr, OffsetOption, static_cast<unsigned int>(strlen(OffsetOption))) || |
645 | 199k | !ASSIMP_strincmp(pPtr, ScaleOption, static_cast<unsigned int>(strlen(ScaleOption))) || |
646 | 199k | !ASSIMP_strincmp(pPtr, TurbulenceOption, static_cast<unsigned int>(strlen(TurbulenceOption)))) { |
647 | 184k | skipToken = 4; |
648 | 184k | } |
649 | | |
650 | 2.56M | for (int i = 0; i < skipToken; ++i) { |
651 | 1.82M | m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
652 | 1.82M | } |
653 | 736k | } |
654 | 937k | } |
655 | | |
656 | | // ------------------------------------------------------------------- |
657 | | |
658 | | } // Namespace Assimp |
659 | | |
660 | | #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER |