Coverage Report

Created: 2025-11-11 06:59

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-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