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