1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2021, assimp team
6 
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
12 following 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 
43 /** @file  MaterialSystem.cpp
44  *  @brief Implementation of the material system of the library
45  */
46 
47 #include "MaterialSystem.h"
48 #include <assimp/Hash.h>
49 #include <assimp/ParsingUtils.h>
50 #include <assimp/fast_atof.h>
51 #include <assimp/material.h>
52 #include <assimp/types.h>
53 #include <assimp/DefaultLogger.hpp>
54 
55 using namespace Assimp;
56 
57 // ------------------------------------------------------------------------------------------------
58 // Get a specific property from a material
aiGetMaterialProperty(const aiMaterial * pMat,const char * pKey,unsigned int type,unsigned int index,const aiMaterialProperty ** pPropOut)59 aiReturn aiGetMaterialProperty(const aiMaterial *pMat,
60         const char *pKey,
61         unsigned int type,
62         unsigned int index,
63         const aiMaterialProperty **pPropOut) {
64     ai_assert(pMat != nullptr);
65     ai_assert(pKey != nullptr);
66     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     for (unsigned int i = 0; i < pMat->mNumProperties; ++i) {
73         aiMaterialProperty *prop = pMat->mProperties[i];
74 
75         if (prop /* just for safety ... */
76                 && 0 == strcmp(prop->mKey.data, pKey) && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */
77                 && (UINT_MAX == index || prop->mIndex == index)) {
78             *pPropOut = pMat->mProperties[i];
79             return AI_SUCCESS;
80         }
81     }
82     *pPropOut = nullptr;
83     return AI_FAILURE;
84 }
85 
86 // ------------------------------------------------------------------------------------------------
87 // Get an array of floating-point values from the material.
aiGetMaterialFloatArray(const aiMaterial * pMat,const char * pKey,unsigned int type,unsigned int index,ai_real * pOut,unsigned int * pMax)88 aiReturn aiGetMaterialFloatArray(const aiMaterial *pMat,
89         const char *pKey,
90         unsigned int type,
91         unsigned int index,
92         ai_real *pOut,
93         unsigned int *pMax) {
94     ai_assert(pOut != nullptr);
95     ai_assert(pMat != nullptr);
96 
97     const aiMaterialProperty *prop;
98     aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
99     if (nullptr == prop) {
100         return AI_FAILURE;
101     }
102 
103     // data is given in floats, convert to ai_real
104     unsigned int iWrite = 0;
105     if (aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) {
106         iWrite = prop->mDataLength / sizeof(float);
107         if (pMax) {
108             iWrite = std::min(*pMax, iWrite);
109             ;
110         }
111 
112         for (unsigned int a = 0; a < iWrite; ++a) {
113             pOut[a] = static_cast<ai_real>(reinterpret_cast<float *>(prop->mData)[a]);
114         }
115 
116         if (pMax) {
117             *pMax = iWrite;
118         }
119     }
120     // data is given in doubles, convert to float
121     else if (aiPTI_Double == prop->mType) {
122         iWrite = prop->mDataLength / sizeof(double);
123         if (pMax) {
124             iWrite = std::min(*pMax, iWrite);
125             ;
126         }
127         for (unsigned int a = 0; a < iWrite; ++a) {
128             pOut[a] = static_cast<ai_real>(reinterpret_cast<double *>(prop->mData)[a]);
129         }
130         if (pMax) {
131             *pMax = iWrite;
132         }
133     }
134     // data is given in ints, convert to float
135     else if (aiPTI_Integer == prop->mType) {
136         iWrite = prop->mDataLength / sizeof(int32_t);
137         if (pMax) {
138             iWrite = std::min(*pMax, iWrite);
139             ;
140         }
141         for (unsigned int a = 0; a < iWrite; ++a) {
142             pOut[a] = static_cast<ai_real>(reinterpret_cast<int32_t *>(prop->mData)[a]);
143         }
144         if (pMax) {
145             *pMax = iWrite;
146         }
147     }
148     // a string ... read floats separated by spaces
149     else {
150         if (pMax) {
151             iWrite = *pMax;
152         }
153         // strings are zero-terminated with a 32 bit length prefix, so this is safe
154         const char *cur = prop->mData + 4;
155         ai_assert(prop->mDataLength >= 5);
156         ai_assert(!prop->mData[prop->mDataLength - 1]);
157         for (unsigned int a = 0;; ++a) {
158             cur = fast_atoreal_move<ai_real>(cur, pOut[a]);
159             if (a == iWrite - 1) {
160                 break;
161             }
162             if (!IsSpace(*cur)) {
163                 ASSIMP_LOG_ERROR("Material property", pKey,
164                                  " is a string; failed to parse a float array out of it.");
165                 return AI_FAILURE;
166             }
167         }
168 
169         if (pMax) {
170             *pMax = iWrite;
171         }
172     }
173     return AI_SUCCESS;
174 }
175 
176 // ------------------------------------------------------------------------------------------------
177 // Get an array if integers from the material
aiGetMaterialIntegerArray(const aiMaterial * pMat,const char * pKey,unsigned int type,unsigned int index,int * pOut,unsigned int * pMax)178 aiReturn aiGetMaterialIntegerArray(const aiMaterial *pMat,
179         const char *pKey,
180         unsigned int type,
181         unsigned int index,
182         int *pOut,
183         unsigned int *pMax) {
184     ai_assert(pOut != nullptr);
185     ai_assert(pMat != nullptr);
186 
187     const aiMaterialProperty *prop;
188     aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
189     if (!prop) {
190         return AI_FAILURE;
191     }
192 
193     // data is given in ints, simply copy it
194     unsigned int iWrite = 0;
195     if (aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) {
196         iWrite = std::max(static_cast<unsigned int>(prop->mDataLength / sizeof(int32_t)), 1u);
197         if (pMax) {
198             iWrite = std::min(*pMax, iWrite);
199         }
200         if (1 == prop->mDataLength) {
201             // bool type, 1 byte
202             *pOut = static_cast<int>(*prop->mData);
203         } else {
204             for (unsigned int a = 0; a < iWrite; ++a) {
205                 pOut[a] = static_cast<int>(reinterpret_cast<int32_t *>(prop->mData)[a]);
206             }
207         }
208         if (pMax) {
209             *pMax = iWrite;
210         }
211     }
212     // data is given in floats convert to int
213     else if (aiPTI_Float == prop->mType) {
214         iWrite = prop->mDataLength / sizeof(float);
215         if (pMax) {
216             iWrite = std::min(*pMax, iWrite);
217             ;
218         }
219         for (unsigned int a = 0; a < iWrite; ++a) {
220             pOut[a] = static_cast<int>(reinterpret_cast<float *>(prop->mData)[a]);
221         }
222         if (pMax) {
223             *pMax = iWrite;
224         }
225     }
226     // it is a string ... no way to read something out of this
227     else {
228         if (pMax) {
229             iWrite = *pMax;
230         }
231         // strings are zero-terminated with a 32 bit length prefix, so this is safe
232         const char *cur = prop->mData + 4;
233         ai_assert(prop->mDataLength >= 5);
234         ai_assert(!prop->mData[prop->mDataLength - 1]);
235         for (unsigned int a = 0;; ++a) {
236             pOut[a] = strtol10(cur, &cur);
237             if (a == iWrite - 1) {
238                 break;
239             }
240             if (!IsSpace(*cur)) {
241                 ASSIMP_LOG_ERROR("Material property", pKey,
242                                  " is a string; failed to parse an integer array out of it.");
243                 return AI_FAILURE;
244             }
245         }
246 
247         if (pMax) {
248             *pMax = iWrite;
249         }
250     }
251     return AI_SUCCESS;
252 }
253 
254 // ------------------------------------------------------------------------------------------------
255 // Get a color (3 or 4 floats) from the material
aiGetMaterialColor(const aiMaterial * pMat,const char * pKey,unsigned int type,unsigned int index,aiColor4D * pOut)256 aiReturn aiGetMaterialColor(const aiMaterial *pMat,
257         const char *pKey,
258         unsigned int type,
259         unsigned int index,
260         aiColor4D *pOut) {
261     unsigned int iMax = 4;
262     const aiReturn eRet = aiGetMaterialFloatArray(pMat, pKey, type, index, (ai_real *)pOut, &iMax);
263 
264     // if no alpha channel is defined: set it to 1.0
265     if (3 == iMax) {
266         pOut->a = 1.0;
267     }
268 
269     return eRet;
270 }
271 
272 // ------------------------------------------------------------------------------------------------
273 // Get a aiUVTransform (5 floats) from the material
aiGetMaterialUVTransform(const aiMaterial * pMat,const char * pKey,unsigned int type,unsigned int index,aiUVTransform * pOut)274 aiReturn aiGetMaterialUVTransform(const aiMaterial *pMat,
275         const char *pKey,
276         unsigned int type,
277         unsigned int index,
278         aiUVTransform *pOut) {
279     unsigned int iMax = 5;
280     return aiGetMaterialFloatArray(pMat, pKey, type, index, (ai_real *)pOut, &iMax);
281 }
282 
283 // ------------------------------------------------------------------------------------------------
284 // Get a string from the material
aiGetMaterialString(const aiMaterial * pMat,const char * pKey,unsigned int type,unsigned int index,aiString * pOut)285 aiReturn aiGetMaterialString(const aiMaterial *pMat,
286         const char *pKey,
287         unsigned int type,
288         unsigned int index,
289         aiString *pOut) {
290     ai_assert(pOut != nullptr);
291 
292     const aiMaterialProperty *prop;
293     aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
294     if (!prop) {
295         return AI_FAILURE;
296     }
297 
298     if (aiPTI_String == prop->mType) {
299         ai_assert(prop->mDataLength >= 5);
300 
301         // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data
302         pOut->length = static_cast<unsigned int>(*reinterpret_cast<uint32_t *>(prop->mData));
303 
304         ai_assert(pOut->length + 1 + 4 == prop->mDataLength);
305         ai_assert(!prop->mData[prop->mDataLength - 1]);
306         memcpy(pOut->data, prop->mData + 4, pOut->length + 1);
307     } else {
308         // TODO - implement lexical cast as well
309         ASSIMP_LOG_ERROR("Material property", pKey, " was found, but is no string");
310         return AI_FAILURE;
311     }
312     return AI_SUCCESS;
313 }
314 
315 // ------------------------------------------------------------------------------------------------
316 // Get the number of textures on a particular texture stack
aiGetMaterialTextureCount(const C_STRUCT aiMaterial * pMat,C_ENUM aiTextureType type)317 unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial *pMat, C_ENUM aiTextureType type) {
318     ai_assert(pMat != nullptr);
319 
320     // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again)
321     unsigned int max = 0;
322     for (unsigned int i = 0; i < pMat->mNumProperties; ++i) {
323         aiMaterialProperty *prop = pMat->mProperties[i];
324 
325         if (prop /* just a sanity check ... */
326                 && 0 == strcmp(prop->mKey.data, _AI_MATKEY_TEXTURE_BASE) && static_cast<aiTextureType>(prop->mSemantic) == type) {
327 
328             max = std::max(max, prop->mIndex + 1);
329         }
330     }
331     return max;
332 }
333 
334 // ------------------------------------------------------------------------------------------------
aiGetMaterialTexture(const C_STRUCT aiMaterial * mat,aiTextureType type,unsigned int index,C_STRUCT aiString * path,aiTextureMapping * _mapping,unsigned int * uvindex,ai_real * blend,aiTextureOp * op,aiTextureMapMode * mapmode,unsigned int * flags)335 aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial *mat,
336         aiTextureType type,
337         unsigned int index,
338         C_STRUCT aiString *path,
339         aiTextureMapping *_mapping /*= nullptr*/,
340         unsigned int *uvindex /*= nullptr*/,
341         ai_real *blend /*= nullptr*/,
342         aiTextureOp *op /*= nullptr*/,
343         aiTextureMapMode *mapmode /*= nullptr*/,
344         unsigned int *flags /*= nullptr*/
345 ) {
346     ai_assert(nullptr != mat);
347     ai_assert(nullptr != path);
348 
349     // Get the path to the texture
350     if (AI_SUCCESS != aiGetMaterialString(mat, AI_MATKEY_TEXTURE(type, index), path)) {
351         return AI_FAILURE;
352     }
353 
354     // Determine mapping type
355     int mapping_ = static_cast<int>(aiTextureMapping_UV);
356     aiGetMaterialInteger(mat, AI_MATKEY_MAPPING(type, index), &mapping_);
357     aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_);
358     if (_mapping)
359         *_mapping = mapping;
360 
361     // Get UV index
362     if (aiTextureMapping_UV == mapping && uvindex) {
363         aiGetMaterialInteger(mat, AI_MATKEY_UVWSRC(type, index), (int *)uvindex);
364     }
365     // Get blend factor
366     if (blend) {
367         aiGetMaterialFloat(mat, AI_MATKEY_TEXBLEND(type, index), blend);
368     }
369     // Get texture operation
370     if (op) {
371         aiGetMaterialInteger(mat, AI_MATKEY_TEXOP(type, index), (int *)op);
372     }
373     // Get texture mapping modes
374     if (mapmode) {
375         aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(type, index), (int *)&mapmode[0]);
376         aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(type, index), (int *)&mapmode[1]);
377     }
378     // Get texture flags
379     if (flags) {
380         aiGetMaterialInteger(mat, AI_MATKEY_TEXFLAGS(type, index), (int *)flags);
381     }
382 
383     return AI_SUCCESS;
384 }
385 
386 static const unsigned int DefaultNumAllocated = 5;
387 
388 // ------------------------------------------------------------------------------------------------
389 // Construction. Actually the one and only way to get an aiMaterial instance
aiMaterial()390 aiMaterial::aiMaterial() :
391         mProperties(nullptr), mNumProperties(0), mNumAllocated(DefaultNumAllocated) {
392     // Allocate 5 entries by default
393     mProperties = new aiMaterialProperty *[DefaultNumAllocated];
394 }
395 
396 // ------------------------------------------------------------------------------------------------
~aiMaterial()397 aiMaterial::~aiMaterial() {
398     Clear();
399 
400     delete[] mProperties;
401 }
402 
403 // ------------------------------------------------------------------------------------------------
GetName() const404 aiString aiMaterial::GetName() const {
405     aiString name;
406     Get(AI_MATKEY_NAME, name);
407 
408     return name;
409 }
410 
411 // ------------------------------------------------------------------------------------------------
Clear()412 void aiMaterial::Clear() {
413     for (unsigned int i = 0; i < mNumProperties; ++i) {
414         // delete this entry
415         delete mProperties[i];
416         AI_DEBUG_INVALIDATE_PTR(mProperties[i]);
417     }
418     mNumProperties = 0;
419 
420     // The array remains allocated, we just invalidated its contents
421 }
422 
423 // ------------------------------------------------------------------------------------------------
RemoveProperty(const char * pKey,unsigned int type,unsigned int index)424 aiReturn aiMaterial::RemoveProperty(const char *pKey, unsigned int type, unsigned int index) {
425     ai_assert(nullptr != pKey);
426 
427     for (unsigned int i = 0; i < mNumProperties; ++i) {
428         aiMaterialProperty *prop = mProperties[i];
429 
430         if (prop && !strcmp(prop->mKey.data, pKey) &&
431                 prop->mSemantic == type && prop->mIndex == index) {
432             // Delete this entry
433             delete mProperties[i];
434 
435             // collapse the array behind --.
436             --mNumProperties;
437             for (unsigned int a = i; a < mNumProperties; ++a) {
438                 mProperties[a] = mProperties[a + 1];
439             }
440             return AI_SUCCESS;
441         }
442     }
443 
444     return AI_FAILURE;
445 }
446 
447 // ------------------------------------------------------------------------------------------------
AddBinaryProperty(const void * pInput,unsigned int pSizeInBytes,const char * pKey,unsigned int type,unsigned int index,aiPropertyTypeInfo pType)448 aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
449         unsigned int pSizeInBytes,
450         const char *pKey,
451         unsigned int type,
452         unsigned int index,
453         aiPropertyTypeInfo pType) {
454     ai_assert(pInput != nullptr);
455     ai_assert(pKey != nullptr);
456     ai_assert(0 != pSizeInBytes);
457 
458     if (0 == pSizeInBytes) {
459         return AI_FAILURE;
460     }
461 
462     // first search the list whether there is already an entry with this key
463     unsigned int iOutIndex(UINT_MAX);
464     for (unsigned int i = 0; i < mNumProperties; ++i) {
465         aiMaterialProperty *prop(mProperties[i]);
466 
467         if (prop /* just for safety */ && !strcmp(prop->mKey.data, pKey) &&
468                 prop->mSemantic == type && prop->mIndex == index) {
469 
470             delete mProperties[i];
471             iOutIndex = i;
472         }
473     }
474 
475     // Allocate a new material property
476     aiMaterialProperty *pcNew = new aiMaterialProperty();
477 
478     // .. and fill it
479     pcNew->mType = pType;
480     pcNew->mSemantic = type;
481     pcNew->mIndex = index;
482 
483     pcNew->mDataLength = pSizeInBytes;
484     pcNew->mData = new char[pSizeInBytes];
485     memcpy(pcNew->mData, pInput, pSizeInBytes);
486 
487     pcNew->mKey.length = static_cast<ai_uint32>(::strlen(pKey));
488     ai_assert(MAXLEN > pcNew->mKey.length);
489     strcpy(pcNew->mKey.data, pKey);
490 
491     if (UINT_MAX != iOutIndex) {
492         mProperties[iOutIndex] = pcNew;
493         return AI_SUCCESS;
494     }
495 
496     // resize the array ... double the storage allocated
497     if (mNumProperties == mNumAllocated) {
498         const unsigned int iOld = mNumAllocated;
499         mNumAllocated *= 2;
500 
501         aiMaterialProperty **ppTemp;
502         try {
503             ppTemp = new aiMaterialProperty *[mNumAllocated];
504         } catch (std::bad_alloc &) {
505             delete pcNew;
506             return AI_OUTOFMEMORY;
507         }
508 
509         // just copy all items over; then replace the old array
510         memcpy(ppTemp, mProperties, iOld * sizeof(void *));
511 
512         delete[] mProperties;
513         mProperties = ppTemp;
514     }
515     // push back ...
516     mProperties[mNumProperties++] = pcNew;
517 
518     return AI_SUCCESS;
519 }
520 
521 // ------------------------------------------------------------------------------------------------
AddProperty(const aiString * pInput,const char * pKey,unsigned int type,unsigned int index)522 aiReturn aiMaterial::AddProperty(const aiString *pInput,
523         const char *pKey,
524         unsigned int type,
525         unsigned int index) {
526     ai_assert(sizeof(ai_uint32) == 4);
527     return AddBinaryProperty(pInput,
528             static_cast<unsigned int>(pInput->length + 1 + 4),
529             pKey,
530             type,
531             index,
532             aiPTI_String);
533 }
534 
535 // ------------------------------------------------------------------------------------------------
ComputeMaterialHash(const aiMaterial * mat,bool includeMatName)536 uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName /*= false*/) {
537     uint32_t hash = 1503; // magic start value, chosen to be my birthday :-)
538     for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
539         aiMaterialProperty *prop;
540 
541         // Exclude all properties whose first character is '?' from the hash
542         // See doc for aiMaterialProperty.
543         prop = mat->mProperties[i];
544         if (nullptr != prop && (includeMatName || prop->mKey.data[0] != '?')) {
545 
546             hash = SuperFastHash(prop->mKey.data, (unsigned int)prop->mKey.length, hash);
547             hash = SuperFastHash(prop->mData, prop->mDataLength, hash);
548 
549             // Combine the semantic and the index with the hash
550             hash = SuperFastHash((const char *)&prop->mSemantic, sizeof(unsigned int), hash);
551             hash = SuperFastHash((const char *)&prop->mIndex, sizeof(unsigned int), hash);
552         }
553     }
554     return hash;
555 }
556 
557 // ------------------------------------------------------------------------------------------------
CopyPropertyList(aiMaterial * const pcDest,const aiMaterial * pcSrc)558 void aiMaterial::CopyPropertyList(aiMaterial *const pcDest,
559         const aiMaterial *pcSrc) {
560     ai_assert(nullptr != pcDest);
561     ai_assert(nullptr != pcSrc);
562     ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated);
563     ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated);
564 
565     const unsigned int iOldNum = pcDest->mNumProperties;
566     pcDest->mNumAllocated += pcSrc->mNumAllocated;
567     pcDest->mNumProperties += pcSrc->mNumProperties;
568 
569     const unsigned int numAllocated = pcDest->mNumAllocated;
570     aiMaterialProperty **pcOld = pcDest->mProperties;
571     pcDest->mProperties = new aiMaterialProperty *[numAllocated];
572 
573     ai_assert(!iOldNum || pcOld);
574     ai_assert(iOldNum < numAllocated);
575 
576     if (iOldNum && pcOld) {
577         for (unsigned int i = 0; i < iOldNum; ++i) {
578             pcDest->mProperties[i] = pcOld[i];
579         }
580     }
581 
582     if (pcOld) {
583         delete[] pcOld;
584     }
585 
586     for (unsigned int i = iOldNum; i < pcDest->mNumProperties; ++i) {
587         aiMaterialProperty *propSrc = pcSrc->mProperties[i];
588 
589         // search whether we have already a property with this name -> if yes, overwrite it
590         aiMaterialProperty *prop;
591         for (unsigned int q = 0; q < iOldNum; ++q) {
592             prop = pcDest->mProperties[q];
593             if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic && prop->mIndex == propSrc->mIndex) {
594                 delete prop;
595 
596                 // collapse the whole array ...
597                 memmove(&pcDest->mProperties[q], &pcDest->mProperties[q + 1], i - q);
598                 i--;
599                 pcDest->mNumProperties--;
600             }
601         }
602 
603         // Allocate the output property and copy the source property
604         prop = pcDest->mProperties[i] = new aiMaterialProperty();
605         prop->mKey = propSrc->mKey;
606         prop->mDataLength = propSrc->mDataLength;
607         prop->mType = propSrc->mType;
608         prop->mSemantic = propSrc->mSemantic;
609         prop->mIndex = propSrc->mIndex;
610 
611         prop->mData = new char[propSrc->mDataLength];
612         memcpy(prop->mData, propSrc->mData, prop->mDataLength);
613     }
614 }
615