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