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