Coverage Report

Created: 2026-01-25 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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