Coverage Report

Created: 2026-05-23 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/Material/MaterialSystem.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
42
/** @file  MaterialSystem.cpp
43
 *  @brief Implementation of the material system of the library
44
 */
45
46
#include "MaterialSystem.h"
47
#include <assimp/Hash.h>
48
#include <assimp/ParsingUtils.h>
49
#include <assimp/fast_atof.h>
50
#include <assimp/material.h>
51
#include <assimp/types.h>
52
#include <assimp/DefaultLogger.hpp>
53
#include <memory>
54
55
using namespace Assimp;
56
57
// ------------------------------------------------------------------------------------------------
58
// Get a specific property from a material
59
aiReturn aiGetMaterialProperty(const aiMaterial *pMat,
60
        const char *pKey,
61
        unsigned int type,
62
        unsigned int index,
63
20.8k
        const aiMaterialProperty **pPropOut) {
64
20.8k
    ai_assert(pMat != nullptr);
65
20.8k
    ai_assert(pKey != nullptr);
66
20.8k
    ai_assert(pPropOut != nullptr);
67
68
    /*  Just search for a property with exactly this name ..
69
     *  could be improved by hashing, but it's possibly
70
     *  no worth the effort (we're bound to C structures,
71
     *  thus std::map or derivates are not applicable. */
72
180k
    for (unsigned int i = 0; i < pMat->mNumProperties; ++i) {
73
169k
        aiMaterialProperty *prop = pMat->mProperties[i];
74
75
169k
        if (prop /* just for safety ... */
76
169k
                && 0 == strncmp(prop->mKey.data, pKey, strlen(pKey)) && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */
77
9.56k
                && (UINT_MAX == index || prop->mIndex == index)) {
78
9.56k
            *pPropOut = pMat->mProperties[i];
79
9.56k
            return AI_SUCCESS;
80
9.56k
        }
81
169k
    }
82
11.2k
    *pPropOut = nullptr;
83
84
11.2k
    return AI_FAILURE;
85
20.8k
}
86
87
namespace {
88
89
// ------------------------------------------------------------------------------------------------
90
// Implementation of functions "aiGetMaterialFloatArray" and "aiGetMaterialFloatFloatArray".
91
template <class TReal>
92
aiReturn GetMaterialFloatArray(const aiMaterial *pMat,
93
        const char *pKey,
94
        unsigned int type,
95
        unsigned int index,
96
        TReal *pOut,
97
14.6k
        unsigned int *pMax) {
98
14.6k
    ai_assert(pOut != nullptr);
99
14.6k
    ai_assert(pMat != nullptr);
100
101
14.6k
    const aiMaterialProperty *prop{nullptr};
102
14.6k
    aiGetMaterialProperty(pMat, pKey, type, index, &prop);
103
14.6k
    if (nullptr == prop) {
104
9.49k
        return AI_FAILURE;
105
9.49k
    }
106
107
    // data is given in floats, convert to TReal
108
5.19k
    unsigned int iWrite = 0;
109
5.19k
    if (aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) {
110
5.19k
        iWrite = prop->mDataLength / sizeof(float);
111
5.19k
        if (pMax) {
112
0
            iWrite = std::min(*pMax, iWrite);
113
0
        }
114
115
10.3k
        for (unsigned int a = 0; a < iWrite; ++a) {
116
5.19k
            pOut[a] = static_cast<TReal>(reinterpret_cast<float *>(prop->mData)[a]);
117
5.19k
        }
118
119
5.19k
        if (pMax) {
120
0
            *pMax = iWrite;
121
0
        }
122
5.19k
    } else if (aiPTI_Double == prop->mType) { // data is given in doubles, convert to TReal
123
0
        iWrite = prop->mDataLength / sizeof(double);
124
0
        if (pMax) {
125
0
            iWrite = std::min(*pMax, iWrite);
126
0
            ;
127
0
        }
128
0
        for (unsigned int a = 0; a < iWrite; ++a) {
129
0
            pOut[a] = static_cast<TReal>(reinterpret_cast<double *>(prop->mData)[a]);
130
0
        }
131
0
        if (pMax) {
132
0
            *pMax = iWrite;
133
0
        }
134
0
    } else if (aiPTI_Integer == prop->mType) { // data is given in ints, convert to TReal
135
0
        iWrite = prop->mDataLength / sizeof(int32_t);
136
0
        if (pMax) {
137
0
            iWrite = std::min(*pMax, iWrite);
138
0
            ;
139
0
        }
140
0
        for (unsigned int a = 0; a < iWrite; ++a) {
141
0
            pOut[a] = static_cast<TReal>(reinterpret_cast<int32_t *>(prop->mData)[a]);
142
0
        }
143
0
        if (pMax) {
144
0
            *pMax = iWrite;
145
0
        }
146
0
    } else { // a string ... read floats separated by spaces
147
0
        if (pMax) {
148
0
            iWrite = *pMax;
149
0
        }
150
        // strings are zero-terminated with a 32 bit length prefix, so this is safe
151
0
        const char *cur = prop->mData + 4;
152
0
        ai_assert(prop->mDataLength >= 5);
153
0
        ai_assert(!prop->mData[prop->mDataLength - 1]);
154
0
        for (unsigned int a = 0;; ++a) {
155
0
            cur = fast_atoreal_move(cur, pOut[a]);
156
0
            if (a == iWrite - 1) {
157
0
                break;
158
0
            }
159
0
            if (!IsSpace(*cur)) {
160
0
                ASSIMP_LOG_ERROR("Material property", pKey,
161
0
                                 " is a string; failed to parse a float array out of it.");
162
0
                return AI_FAILURE;
163
0
            }
164
0
        }
165
166
0
        if (pMax) {
167
0
            *pMax = iWrite;
168
0
        }
169
0
    }
170
171
5.19k
    return AI_SUCCESS;
172
5.19k
}
173
174
// ------------------------------------------------------------------------------------------------
175
// Get an array of float typed float values from the material.
176
aiReturn aiGetMaterialFloatFloatArray(const aiMaterial *pMat,
177
        const char *pKey,
178
        unsigned int type,
179
        unsigned int index,
180
        float *pOut,
181
0
        unsigned int *pMax) {
182
0
    return GetMaterialFloatArray(pMat, pKey, type, index, pOut, pMax);
183
0
}
184
185
} // namespace
186
187
// ------------------------------------------------------------------------------------------------
188
// Get an array of floating-point values from the material.
189
aiReturn aiGetMaterialFloatArray(const aiMaterial *pMat,
190
        const char *pKey,
191
        unsigned int type,
192
        unsigned int index,
193
        ai_real *pOut,
194
14.6k
        unsigned int *pMax) {
195
14.6k
    return GetMaterialFloatArray(pMat, pKey, type, index, pOut, pMax);
196
14.6k
}
197
198
// ------------------------------------------------------------------------------------------------
199
// Get an array if integers from the material
200
aiReturn aiGetMaterialIntegerArray(const aiMaterial *pMat,
201
        const char *pKey,
202
        unsigned int type,
203
        unsigned int index,
204
        int *pOut,
205
6.13k
        unsigned int *pMax) {
206
6.13k
    ai_assert(pOut != nullptr);
207
6.13k
    ai_assert(pMat != nullptr);
208
209
6.13k
    const aiMaterialProperty *prop;
210
6.13k
    aiGetMaterialProperty(pMat, pKey, type, index, &prop);
211
6.13k
    if (!prop) {
212
1.77k
        return AI_FAILURE;
213
1.77k
    }
214
215
    // data is given in ints, simply copy it
216
4.36k
    unsigned int iWrite = 0;
217
4.36k
    if (aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) {
218
4.36k
        iWrite = std::max(static_cast<unsigned int>(prop->mDataLength / sizeof(int32_t)), 1u);
219
4.36k
        if (pMax) {
220
0
            iWrite = std::min(*pMax, iWrite);
221
0
        }
222
4.36k
        if (1 == prop->mDataLength) {
223
            // bool type, 1 byte
224
0
            *pOut = static_cast<int>(*prop->mData);
225
4.36k
        } else {
226
8.73k
            for (unsigned int a = 0; a < iWrite; ++a) {
227
4.36k
                pOut[a] = static_cast<int>(reinterpret_cast<int32_t *>(prop->mData)[a]);
228
4.36k
            }
229
4.36k
        }
230
4.36k
        if (pMax) {
231
0
            *pMax = iWrite;
232
0
        }
233
4.36k
    }
234
    // data is given in floats convert to int
235
0
    else if (aiPTI_Float == prop->mType) {
236
0
        iWrite = prop->mDataLength / sizeof(float);
237
0
        if (pMax) {
238
0
            iWrite = std::min(*pMax, iWrite);
239
0
            ;
240
0
        }
241
0
        for (unsigned int a = 0; a < iWrite; ++a) {
242
0
            pOut[a] = static_cast<int>(reinterpret_cast<float *>(prop->mData)[a]);
243
0
        }
244
0
        if (pMax) {
245
0
            *pMax = iWrite;
246
0
        }
247
0
    }
248
    // it is a string ... no way to read something out of this
249
0
    else {
250
0
        if (pMax) {
251
0
            iWrite = *pMax;
252
0
        }
253
        // strings are zero-terminated with a 32 bit length prefix, so this is safe
254
0
        const char *cur = prop->mData + 4;
255
0
        ai_assert(prop->mDataLength >= 5);
256
0
        ai_assert(!prop->mData[prop->mDataLength - 1]);
257
0
        for (unsigned int a = 0;; ++a) {
258
0
            pOut[a] = strtol10(cur, &cur);
259
0
            if (a == iWrite - 1) {
260
0
                break;
261
0
            }
262
0
            if (!IsSpace(*cur)) {
263
0
                ASSIMP_LOG_ERROR("Material property", pKey,
264
0
                                 " is a string; failed to parse an integer array out of it.");
265
0
                return AI_FAILURE;
266
0
            }
267
0
        }
268
269
0
        if (pMax) {
270
0
            *pMax = iWrite;
271
0
        }
272
0
    }
273
4.36k
    return AI_SUCCESS;
274
4.36k
}
275
276
// ------------------------------------------------------------------------------------------------
277
// Get a color (3 or 4 floats) from the material
278
aiReturn aiGetMaterialColor(const aiMaterial *pMat,
279
        const char *pKey,
280
        unsigned int type,
281
        unsigned int index,
282
0
        aiColor4D *pOut) {
283
0
    unsigned int iMax = 4;
284
0
    const aiReturn eRet = aiGetMaterialFloatFloatArray(pMat, pKey, type, index, (float *)pOut, &iMax);
285
286
    // if no alpha channel is defined: set it to 1.0
287
0
    if (3 == iMax) {
288
0
        pOut->a = 1.0;
289
0
    }
290
291
0
    return eRet;
292
0
}
293
294
// ------------------------------------------------------------------------------------------------
295
// Get a aiUVTransform (5 floats) from the material
296
aiReturn aiGetMaterialUVTransform(const aiMaterial *pMat,
297
        const char *pKey,
298
        unsigned int type,
299
        unsigned int index,
300
0
        aiUVTransform *pOut) {
301
0
    unsigned int iMax = 5;
302
0
    return aiGetMaterialFloatArray(pMat, pKey, type, index, reinterpret_cast<ai_real *>(pOut), &iMax);
303
0
}
304
305
// ------------------------------------------------------------------------------------------------
306
// Get a string from the material
307
aiReturn aiGetMaterialString(const aiMaterial *pMat,
308
        const char *pKey,
309
        unsigned int type,
310
        unsigned int index,
311
0
        aiString *pOut) {
312
0
    ai_assert(pOut != nullptr);
313
314
0
    const aiMaterialProperty *prop{nullptr};
315
0
    aiGetMaterialProperty(pMat, pKey, type, index, &prop);
316
0
    if (!prop) {
317
0
        return AI_FAILURE;
318
0
    }
319
320
0
    if (aiPTI_String == prop->mType) {
321
0
        ai_assert(prop->mDataLength >= 5);
322
323
        // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data
324
0
        pOut->length = static_cast<unsigned int>(*reinterpret_cast<uint32_t *>(prop->mData));
325
326
0
        ai_assert(pOut->length + 1 + 4 == prop->mDataLength);
327
0
        ai_assert(!prop->mData[prop->mDataLength - 1]);
328
0
        memcpy(pOut->data, prop->mData + 4, pOut->length + 1);
329
0
    } else {
330
        // TODO - implement lexical cast as well
331
0
        ASSIMP_LOG_ERROR("Material property", pKey, " was found, but is no string");
332
0
        return AI_FAILURE;
333
0
    }
334
0
    return AI_SUCCESS;
335
0
}
336
337
// ------------------------------------------------------------------------------------------------
338
// Get a c-like string fron an aiString
339
0
const char *aiGetStringC_Str(const aiString *str) {
340
0
  return str->data;
341
0
}
342
343
// ------------------------------------------------------------------------------------------------
344
// Get the number of textures on a particular texture stack
345
0
unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial *pMat, C_ENUM aiTextureType type) {
346
0
    ai_assert(pMat != nullptr);
347
348
    // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again)
349
0
    unsigned int max = 0;
350
0
    for (unsigned int i = 0; i < pMat->mNumProperties; ++i) {
351
0
        aiMaterialProperty *prop = pMat->mProperties[i];
352
353
0
        if (prop /* just a sanity check ... */
354
0
                && 0 == strcmp(prop->mKey.data, _AI_MATKEY_TEXTURE_BASE) && static_cast<aiTextureType>(prop->mSemantic) == type) {
355
356
0
            max = std::max(max, prop->mIndex + 1);
357
0
        }
358
0
    }
359
0
    return max;
360
0
}
361
362
// ------------------------------------------------------------------------------------------------
363
aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial *mat,
364
        aiTextureType type,
365
        unsigned int index,
366
        C_STRUCT aiString *path,
367
        aiTextureMapping *_mapping /*= nullptr*/,
368
        unsigned int *uvindex /*= nullptr*/,
369
        ai_real *blend /*= nullptr*/,
370
        aiTextureOp *op /*= nullptr*/,
371
        aiTextureMapMode *mapmode /*= nullptr*/,
372
        unsigned int *flags /*= nullptr*/
373
0
) {
374
0
    ai_assert(nullptr != mat);
375
0
    ai_assert(nullptr != path);
376
377
    // Get the path to the texture
378
0
    if (AI_SUCCESS != aiGetMaterialString(mat, AI_MATKEY_TEXTURE(type, index), path)) {
379
0
        return AI_FAILURE;
380
0
    }
381
382
    // Determine mapping type
383
0
    int mapping_ = aiTextureMapping_UV;
384
0
    aiGetMaterialInteger(mat, AI_MATKEY_MAPPING(type, index), &mapping_);
385
0
    aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_);
386
0
    if (_mapping)
387
0
        *_mapping = mapping;
388
389
    // Get UV index
390
0
    if (aiTextureMapping_UV == mapping && uvindex) {
391
0
        aiGetMaterialInteger(mat, AI_MATKEY_UVWSRC(type, index), (int *)uvindex);
392
0
    }
393
    // Get blend factor
394
0
    if (blend) {
395
0
        aiGetMaterialFloat(mat, AI_MATKEY_TEXBLEND(type, index), blend);
396
0
    }
397
    // Get texture operation
398
0
    if (op) {
399
0
        aiGetMaterialInteger(mat, AI_MATKEY_TEXOP(type, index), (int *)op);
400
0
    }
401
    // Get texture mapping modes
402
0
    if (mapmode) {
403
0
        aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(type, index), (int *)&mapmode[0]);
404
0
        aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(type, index), (int *)&mapmode[1]);
405
0
    }
406
    // Get texture flags
407
0
    if (flags) {
408
0
        aiGetMaterialInteger(mat, AI_MATKEY_TEXFLAGS(type, index), (int *)flags);
409
0
    }
410
411
0
    return AI_SUCCESS;
412
0
}
413
414
static constexpr unsigned int DefaultNumAllocated = 5;
415
416
// ------------------------------------------------------------------------------------------------
417
// Construction. Actually the one and only way to get an aiMaterial instance
418
8.00k
aiMaterial::aiMaterial() : mProperties(nullptr), mNumProperties(0), mNumAllocated(DefaultNumAllocated) {
419
    // Allocate 5 entries by default
420
8.00k
    mProperties = new aiMaterialProperty *[DefaultNumAllocated];
421
8.00k
}
422
423
// ------------------------------------------------------------------------------------------------
424
7.96k
aiMaterial::~aiMaterial() {
425
7.96k
    Clear();
426
427
7.96k
    delete[] mProperties;
428
7.96k
}
429
430
// ------------------------------------------------------------------------------------------------
431
0
aiString aiMaterial::GetName() const {
432
0
    aiString name{};
433
0
    Get(AI_MATKEY_NAME, name);
434
435
0
    return name;
436
0
}
437
438
// ------------------------------------------------------------------------------------------------
439
7.96k
void aiMaterial::Clear() {
440
86.9k
    for (unsigned int i = 0; i < mNumProperties; ++i) {
441
        // delete this entry
442
78.9k
        delete mProperties[i];
443
78.9k
        AI_DEBUG_INVALIDATE_PTR(mProperties[i]);
444
78.9k
    }
445
7.96k
    mNumProperties = 0;
446
447
    // The array remains allocated, we just invalidated its contents
448
7.96k
}
449
450
// ------------------------------------------------------------------------------------------------
451
0
aiReturn aiMaterial::RemoveProperty(const char *pKey, unsigned int type, unsigned int index) {
452
0
    ai_assert(nullptr != pKey);
453
454
0
    for (unsigned int i = 0; i < mNumProperties; ++i) {
455
0
        aiMaterialProperty *prop = mProperties[i];
456
457
0
        if (prop && !strcmp(prop->mKey.data, pKey) &&
458
0
                prop->mSemantic == type && prop->mIndex == index) {
459
            // Delete this entry
460
0
            delete mProperties[i];
461
462
            // collapse the array behind --.
463
0
            --mNumProperties;
464
0
            for (unsigned int a = i; a < mNumProperties; ++a) {
465
0
                mProperties[a] = mProperties[a + 1];
466
0
            }
467
0
            return AI_SUCCESS;
468
0
        }
469
0
    }
470
471
0
    return AI_FAILURE;
472
0
}
473
474
// ------------------------------------------------------------------------------------------------
475
aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
476
        unsigned int pSizeInBytes,
477
        const char *pKey,
478
        unsigned int type,
479
        unsigned int index,
480
79.9k
        aiPropertyTypeInfo pType) {
481
79.9k
    ai_assert(pInput != nullptr);
482
79.9k
    ai_assert(pKey != nullptr);
483
79.9k
    ai_assert(0 != pSizeInBytes);
484
485
79.9k
    if (0 == pSizeInBytes) {
486
0
        return AI_FAILURE;
487
0
    }
488
489
    // first search the list whether there is already an entry with this key
490
79.9k
    unsigned int iOutIndex(UINT_MAX);
491
562k
    for (unsigned int i = 0; i < mNumProperties; ++i) {
492
482k
        aiMaterialProperty *prop(mProperties[i]);
493
494
482k
        if (prop /* just for safety */ && !strcmp(prop->mKey.data, pKey) &&
495
991
                prop->mSemantic == type && prop->mIndex == index) {
496
497
823
            delete mProperties[i];
498
823
            iOutIndex = i;
499
823
        }
500
482k
    }
501
502
    // Allocate a new material property
503
79.9k
    auto pcNew = std::make_unique<aiMaterialProperty>();
504
505
    // .. and fill it
506
79.9k
    pcNew->mType = pType;
507
79.9k
    pcNew->mSemantic = type;
508
79.9k
    pcNew->mIndex = index;
509
510
79.9k
    pcNew->mDataLength = pSizeInBytes;
511
79.9k
    pcNew->mData = new char[pSizeInBytes];
512
79.9k
    memcpy(pcNew->mData, pInput, pSizeInBytes);
513
514
79.9k
    const size_t keyLen = ::strlen(pKey);
515
79.9k
    pcNew->mKey.length = static_cast<ai_uint32>(std::min<size_t>(keyLen, AI_MAXLEN - 1));
516
79.9k
    if (keyLen >= AI_MAXLEN) {
517
0
        ASSIMP_LOG_WARN("aiMaterial: property key '", pKey, "' exceeds AI_MAXLEN and will be truncated.");
518
0
    }
519
79.9k
  memcpy(pcNew->mKey.data, pKey, pcNew->mKey.length);
520
79.9k
    pcNew->mKey.data[pcNew->mKey.length] = '\0';
521
522
79.9k
    if (UINT_MAX != iOutIndex) {
523
823
        mProperties[iOutIndex] = pcNew.release();
524
823
        return AI_SUCCESS;
525
823
    }
526
527
    // resize the array ... double the storage allocated
528
79.0k
    if (mNumProperties == mNumAllocated) {
529
11.4k
        const unsigned int iOld = mNumAllocated;
530
11.4k
        mNumAllocated *= 2;
531
532
11.4k
        aiMaterialProperty **ppTemp;
533
11.4k
        try {
534
11.4k
            ppTemp = new aiMaterialProperty *[mNumAllocated];
535
11.4k
        } catch (std::bad_alloc &) {
536
0
            return AI_OUTOFMEMORY;
537
0
        }
538
539
        // just copy all items over; then replace the old array
540
11.4k
        memcpy(ppTemp, mProperties, iOld * sizeof(void *));
541
542
11.4k
        delete[] mProperties;
543
11.4k
        mProperties = ppTemp;
544
11.4k
    }
545
    // push back ...
546
79.0k
    mProperties[mNumProperties++] = pcNew.release();
547
548
79.0k
    return AI_SUCCESS;
549
79.0k
}
550
551
// ------------------------------------------------------------------------------------------------
552
aiReturn aiMaterial::AddProperty(const aiString *pInput,
553
        const char *pKey,
554
        unsigned int type,
555
8.83k
        unsigned int index) {
556
8.83k
    ai_assert(sizeof(ai_uint32) == 4);
557
8.83k
    return AddBinaryProperty(pInput,
558
8.83k
            static_cast<unsigned int>(pInput->length + 1 + 4),
559
8.83k
            pKey,
560
8.83k
            type,
561
8.83k
            index,
562
8.83k
            aiPTI_String);
563
8.83k
}
564
565
// ------------------------------------------------------------------------------------------------
566
3.22k
uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName /*= false*/) {
567
3.22k
    uint32_t hash = 1503; // magic start value, chosen to be my birthday :-)
568
25.9k
    for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
569
        // Exclude all properties whose first character is '?' from the hash
570
        // See doc for aiMaterialProperty.
571
22.7k
        const aiMaterialProperty *prop = mat->mProperties[i];
572
22.7k
        if (nullptr != prop && (includeMatName || prop->mKey.data[0] != '?')) {
573
574
19.5k
            hash = SuperFastHash(prop->mKey.data, (unsigned int)prop->mKey.length, hash);
575
19.5k
            hash = SuperFastHash(prop->mData, prop->mDataLength, hash);
576
577
            // Combine the semantic and the index with the hash
578
19.5k
            hash = SuperFastHash((const char *)&prop->mSemantic, sizeof(unsigned int), hash);
579
19.5k
            hash = SuperFastHash((const char *)&prop->mIndex, sizeof(unsigned int), hash);
580
19.5k
        }
581
22.7k
    }
582
3.22k
    return hash;
583
3.22k
}
584
585
// ------------------------------------------------------------------------------------------------
586
void aiMaterial::CopyPropertyList(aiMaterial *const pcDest,
587
0
        const aiMaterial *pcSrc) {
588
0
    ai_assert(nullptr != pcDest);
589
0
    ai_assert(nullptr != pcSrc);
590
0
    ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated);
591
0
    ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated);
592
593
0
    const unsigned int iOldNum = pcDest->mNumProperties;
594
0
    pcDest->mNumAllocated += pcSrc->mNumAllocated;
595
0
    pcDest->mNumProperties += pcSrc->mNumProperties;
596
597
0
    const unsigned int numAllocated = pcDest->mNumAllocated;
598
0
    aiMaterialProperty **pcOld = pcDest->mProperties;
599
0
    pcDest->mProperties = new aiMaterialProperty *[numAllocated];
600
601
0
    ai_assert(!iOldNum || pcOld);
602
0
    ai_assert(iOldNum < numAllocated);
603
604
0
    if (iOldNum && pcOld) {
605
0
        for (unsigned int i = 0; i < iOldNum; ++i) {
606
0
            pcDest->mProperties[i] = pcOld[i];
607
0
        }
608
0
    }
609
610
0
    if (pcOld) {
611
0
        delete[] pcOld;
612
0
    }
613
614
0
    for (unsigned int i = iOldNum; i < pcDest->mNumProperties; ++i) {
615
0
        const aiMaterialProperty *propSrc = pcSrc->mProperties[i];
616
617
        // search whether we have already a property with this name -> if yes, overwrite it
618
0
        aiMaterialProperty *prop;
619
0
        for (unsigned int q = 0; q < iOldNum; ++q) {
620
0
            prop = pcDest->mProperties[q];
621
0
            if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic && prop->mIndex == propSrc->mIndex) {
622
0
                delete prop;
623
624
                // collapse the whole array ...
625
0
                memmove(&pcDest->mProperties[q], &pcDest->mProperties[q + 1], i - q);
626
0
                i--;
627
0
                pcDest->mNumProperties--;
628
0
            }
629
0
        }
630
631
        // Allocate the output property and copy the source property
632
0
        prop = pcDest->mProperties[i] = new aiMaterialProperty();
633
0
        prop->mKey = propSrc->mKey;
634
0
        prop->mDataLength = propSrc->mDataLength;
635
0
        prop->mType = propSrc->mType;
636
0
        prop->mSemantic = propSrc->mSemantic;
637
0
        prop->mIndex = propSrc->mIndex;
638
639
0
        prop->mData = new char[propSrc->mDataLength];
640
0
        memcpy(prop->mData, propSrc->mData, prop->mDataLength);
641
0
    }
642
0
}