123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- /*
- Open Asset Import Library (assimp)
- ----------------------------------------------------------------------
- Copyright (c) 2006-2020, assimp team
- All rights reserved.
- Redistribution and use of this software in source and binary forms,
- with or without modification, are permitted provided that the
- following conditions are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
- * Neither the name of the assimp team, nor the names of its
- contributors may be used to endorse or promote products
- derived from this software without specific prior
- written permission of the assimp team.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- ----------------------------------------------------------------------
- */
- /** @file MaterialSystem.cpp
- * @brief Implementation of the material system of the library
- */
- #include "MaterialSystem.h"
- #include <assimp/Hash.h>
- #include <assimp/ParsingUtils.h>
- #include <assimp/fast_atof.h>
- #include <assimp/material.h>
- #include <assimp/types.h>
- #include <assimp/DefaultLogger.hpp>
- using namespace Assimp;
- // ------------------------------------------------------------------------------------------------
- // Get a specific property from a material
- aiReturn aiGetMaterialProperty(const aiMaterial *pMat,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- const aiMaterialProperty **pPropOut) {
- ai_assert(pMat != nullptr);
- ai_assert(pKey != nullptr);
- ai_assert(pPropOut != nullptr);
- /* Just search for a property with exactly this name ..
- * could be improved by hashing, but it's possibly
- * no worth the effort (we're bound to C structures,
- * thus std::map or derivates are not applicable. */
- for (unsigned int i = 0; i < pMat->mNumProperties; ++i) {
- aiMaterialProperty *prop = pMat->mProperties[i];
- if (prop /* just for safety ... */
- && 0 == strcmp(prop->mKey.data, pKey) && (UINT_MAX == type || prop->mSemantic == type) /* UINT_MAX is a wild-card, but this is undocumented :-) */
- && (UINT_MAX == index || prop->mIndex == index)) {
- *pPropOut = pMat->mProperties[i];
- return AI_SUCCESS;
- }
- }
- *pPropOut = nullptr;
- return AI_FAILURE;
- }
- // ------------------------------------------------------------------------------------------------
- // Get an array of floating-point values from the material.
- aiReturn aiGetMaterialFloatArray(const aiMaterial *pMat,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- ai_real *pOut,
- unsigned int *pMax) {
- ai_assert(pOut != nullptr);
- ai_assert(pMat != nullptr);
- const aiMaterialProperty *prop;
- aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
- if (nullptr == prop) {
- return AI_FAILURE;
- }
- // data is given in floats, convert to ai_real
- unsigned int iWrite = 0;
- if (aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) {
- iWrite = prop->mDataLength / sizeof(float);
- if (pMax) {
- iWrite = std::min(*pMax, iWrite);
- ;
- }
- for (unsigned int a = 0; a < iWrite; ++a) {
- pOut[a] = static_cast<ai_real>(reinterpret_cast<float *>(prop->mData)[a]);
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- // data is given in doubles, convert to float
- else if (aiPTI_Double == prop->mType) {
- iWrite = prop->mDataLength / sizeof(double);
- if (pMax) {
- iWrite = std::min(*pMax, iWrite);
- ;
- }
- for (unsigned int a = 0; a < iWrite; ++a) {
- pOut[a] = static_cast<ai_real>(reinterpret_cast<double *>(prop->mData)[a]);
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- // data is given in ints, convert to float
- else if (aiPTI_Integer == prop->mType) {
- iWrite = prop->mDataLength / sizeof(int32_t);
- if (pMax) {
- iWrite = std::min(*pMax, iWrite);
- ;
- }
- for (unsigned int a = 0; a < iWrite; ++a) {
- pOut[a] = static_cast<ai_real>(reinterpret_cast<int32_t *>(prop->mData)[a]);
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- // a string ... read floats separated by spaces
- else {
- if (pMax) {
- iWrite = *pMax;
- }
- // strings are zero-terminated with a 32 bit length prefix, so this is safe
- const char *cur = prop->mData + 4;
- ai_assert(prop->mDataLength >= 5);
- ai_assert(!prop->mData[prop->mDataLength - 1]);
- for (unsigned int a = 0;; ++a) {
- cur = fast_atoreal_move<ai_real>(cur, pOut[a]);
- if (a == iWrite - 1) {
- break;
- }
- if (!IsSpace(*cur)) {
- ASSIMP_LOG_ERROR("Material property" + std::string(pKey) +
- " is a string; failed to parse a float array out of it.");
- return AI_FAILURE;
- }
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- return AI_SUCCESS;
- }
- // ------------------------------------------------------------------------------------------------
- // Get an array if integers from the material
- aiReturn aiGetMaterialIntegerArray(const aiMaterial *pMat,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- int *pOut,
- unsigned int *pMax) {
- ai_assert(pOut != nullptr);
- ai_assert(pMat != nullptr);
- const aiMaterialProperty *prop;
- aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
- if (!prop) {
- return AI_FAILURE;
- }
- // data is given in ints, simply copy it
- unsigned int iWrite = 0;
- if (aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) {
- iWrite = std::max(static_cast<unsigned int>(prop->mDataLength / sizeof(int32_t)), 1u);
- if (pMax) {
- iWrite = std::min(*pMax, iWrite);
- }
- if (1 == prop->mDataLength) {
- // bool type, 1 byte
- *pOut = static_cast<int>(*prop->mData);
- } else {
- for (unsigned int a = 0; a < iWrite; ++a) {
- pOut[a] = static_cast<int>(reinterpret_cast<int32_t *>(prop->mData)[a]);
- }
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- // data is given in floats convert to int
- else if (aiPTI_Float == prop->mType) {
- iWrite = prop->mDataLength / sizeof(float);
- if (pMax) {
- iWrite = std::min(*pMax, iWrite);
- ;
- }
- for (unsigned int a = 0; a < iWrite; ++a) {
- pOut[a] = static_cast<int>(reinterpret_cast<float *>(prop->mData)[a]);
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- // it is a string ... no way to read something out of this
- else {
- if (pMax) {
- iWrite = *pMax;
- }
- // strings are zero-terminated with a 32 bit length prefix, so this is safe
- const char *cur = prop->mData + 4;
- ai_assert(prop->mDataLength >= 5);
- ai_assert(!prop->mData[prop->mDataLength - 1]);
- for (unsigned int a = 0;; ++a) {
- pOut[a] = strtol10(cur, &cur);
- if (a == iWrite - 1) {
- break;
- }
- if (!IsSpace(*cur)) {
- ASSIMP_LOG_ERROR("Material property" + std::string(pKey) +
- " is a string; failed to parse an integer array out of it.");
- return AI_FAILURE;
- }
- }
- if (pMax) {
- *pMax = iWrite;
- }
- }
- return AI_SUCCESS;
- }
- // ------------------------------------------------------------------------------------------------
- // Get a color (3 or 4 floats) from the material
- aiReturn aiGetMaterialColor(const aiMaterial *pMat,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- aiColor4D *pOut) {
- unsigned int iMax = 4;
- const aiReturn eRet = aiGetMaterialFloatArray(pMat, pKey, type, index, (ai_real *)pOut, &iMax);
- // if no alpha channel is defined: set it to 1.0
- if (3 == iMax) {
- pOut->a = 1.0;
- }
- return eRet;
- }
- // ------------------------------------------------------------------------------------------------
- // Get a aiUVTransform (5 floats) from the material
- aiReturn aiGetMaterialUVTransform(const aiMaterial *pMat,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- aiUVTransform *pOut) {
- unsigned int iMax = 5;
- return aiGetMaterialFloatArray(pMat, pKey, type, index, (ai_real *)pOut, &iMax);
- }
- // ------------------------------------------------------------------------------------------------
- // Get a string from the material
- aiReturn aiGetMaterialString(const aiMaterial *pMat,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- aiString *pOut) {
- ai_assert(pOut != nullptr);
- const aiMaterialProperty *prop;
- aiGetMaterialProperty(pMat, pKey, type, index, (const aiMaterialProperty **)&prop);
- if (!prop) {
- return AI_FAILURE;
- }
- if (aiPTI_String == prop->mType) {
- ai_assert(prop->mDataLength >= 5);
- // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data
- pOut->length = static_cast<unsigned int>(*reinterpret_cast<uint32_t *>(prop->mData));
- ai_assert(pOut->length + 1 + 4 == prop->mDataLength);
- ai_assert(!prop->mData[prop->mDataLength - 1]);
- memcpy(pOut->data, prop->mData + 4, pOut->length + 1);
- } else {
- // TODO - implement lexical cast as well
- ASSIMP_LOG_ERROR("Material property" + std::string(pKey) +
- " was found, but is no string");
- return AI_FAILURE;
- }
- return AI_SUCCESS;
- }
- // ------------------------------------------------------------------------------------------------
- // Get the number of textures on a particular texture stack
- unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial *pMat, C_ENUM aiTextureType type) {
- ai_assert(pMat != nullptr);
- // Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again)
- unsigned int max = 0;
- for (unsigned int i = 0; i < pMat->mNumProperties; ++i) {
- aiMaterialProperty *prop = pMat->mProperties[i];
- if (prop /* just a sanity check ... */
- && 0 == strcmp(prop->mKey.data, _AI_MATKEY_TEXTURE_BASE) && static_cast<aiTextureType>(prop->mSemantic) == type) {
- max = std::max(max, prop->mIndex + 1);
- }
- }
- return max;
- }
- // ------------------------------------------------------------------------------------------------
- aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial *mat,
- aiTextureType type,
- unsigned int index,
- C_STRUCT aiString *path,
- aiTextureMapping *_mapping /*= nullptr*/,
- unsigned int *uvindex /*= nullptr*/,
- ai_real *blend /*= nullptr*/,
- aiTextureOp *op /*= nullptr*/,
- aiTextureMapMode *mapmode /*= nullptr*/,
- unsigned int *flags /*= nullptr*/
- ) {
- ai_assert(nullptr != mat);
- ai_assert(nullptr != path);
- // Get the path to the texture
- if (AI_SUCCESS != aiGetMaterialString(mat, AI_MATKEY_TEXTURE(type, index), path)) {
- return AI_FAILURE;
- }
- // Determine mapping type
- int mapping_ = static_cast<int>(aiTextureMapping_UV);
- aiGetMaterialInteger(mat, AI_MATKEY_MAPPING(type, index), &mapping_);
- aiTextureMapping mapping = static_cast<aiTextureMapping>(mapping_);
- if (_mapping)
- *_mapping = mapping;
- // Get UV index
- if (aiTextureMapping_UV == mapping && uvindex) {
- aiGetMaterialInteger(mat, AI_MATKEY_UVWSRC(type, index), (int *)uvindex);
- }
- // Get blend factor
- if (blend) {
- aiGetMaterialFloat(mat, AI_MATKEY_TEXBLEND(type, index), blend);
- }
- // Get texture operation
- if (op) {
- aiGetMaterialInteger(mat, AI_MATKEY_TEXOP(type, index), (int *)op);
- }
- // Get texture mapping modes
- if (mapmode) {
- aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(type, index), (int *)&mapmode[0]);
- aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(type, index), (int *)&mapmode[1]);
- }
- // Get texture flags
- if (flags) {
- aiGetMaterialInteger(mat, AI_MATKEY_TEXFLAGS(type, index), (int *)flags);
- }
- return AI_SUCCESS;
- }
- static const unsigned int DefaultNumAllocated = 5;
- // ------------------------------------------------------------------------------------------------
- // Construction. Actually the one and only way to get an aiMaterial instance
- aiMaterial::aiMaterial() :
- mProperties(nullptr), mNumProperties(0), mNumAllocated(DefaultNumAllocated) {
- // Allocate 5 entries by default
- mProperties = new aiMaterialProperty *[DefaultNumAllocated];
- }
- // ------------------------------------------------------------------------------------------------
- aiMaterial::~aiMaterial() {
- Clear();
- delete[] mProperties;
- }
- // ------------------------------------------------------------------------------------------------
- aiString aiMaterial::GetName() {
- aiString name;
- Get(AI_MATKEY_NAME, name);
- return name;
- }
- // ------------------------------------------------------------------------------------------------
- void aiMaterial::Clear() {
- for (unsigned int i = 0; i < mNumProperties; ++i) {
- // delete this entry
- delete mProperties[i];
- AI_DEBUG_INVALIDATE_PTR(mProperties[i]);
- }
- mNumProperties = 0;
- // The array remains allocated, we just invalidated its contents
- }
- // ------------------------------------------------------------------------------------------------
- aiReturn aiMaterial::RemoveProperty(const char *pKey, unsigned int type, unsigned int index) {
- ai_assert(nullptr != pKey);
- for (unsigned int i = 0; i < mNumProperties; ++i) {
- aiMaterialProperty *prop = mProperties[i];
- if (prop && !strcmp(prop->mKey.data, pKey) &&
- prop->mSemantic == type && prop->mIndex == index) {
- // Delete this entry
- delete mProperties[i];
- // collapse the array behind --.
- --mNumProperties;
- for (unsigned int a = i; a < mNumProperties; ++a) {
- mProperties[a] = mProperties[a + 1];
- }
- return AI_SUCCESS;
- }
- }
- return AI_FAILURE;
- }
- // ------------------------------------------------------------------------------------------------
- aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
- unsigned int pSizeInBytes,
- const char *pKey,
- unsigned int type,
- unsigned int index,
- aiPropertyTypeInfo pType) {
- ai_assert(pInput != nullptr);
- ai_assert(pKey != nullptr);
- ai_assert(0 != pSizeInBytes);
- if (0 == pSizeInBytes) {
- return AI_FAILURE;
- }
- // first search the list whether there is already an entry with this key
- unsigned int iOutIndex(UINT_MAX);
- for (unsigned int i = 0; i < mNumProperties; ++i) {
- aiMaterialProperty *prop(mProperties[i]);
- if (prop /* just for safety */ && !strcmp(prop->mKey.data, pKey) &&
- prop->mSemantic == type && prop->mIndex == index) {
- delete mProperties[i];
- iOutIndex = i;
- }
- }
- // Allocate a new material property
- aiMaterialProperty *pcNew = new aiMaterialProperty();
- // .. and fill it
- pcNew->mType = pType;
- pcNew->mSemantic = type;
- pcNew->mIndex = index;
- pcNew->mDataLength = pSizeInBytes;
- pcNew->mData = new char[pSizeInBytes];
- memcpy(pcNew->mData, pInput, pSizeInBytes);
- pcNew->mKey.length = static_cast<ai_uint32>(::strlen(pKey));
- ai_assert(MAXLEN > pcNew->mKey.length);
- strcpy(pcNew->mKey.data, pKey);
- if (UINT_MAX != iOutIndex) {
- mProperties[iOutIndex] = pcNew;
- return AI_SUCCESS;
- }
- // resize the array ... double the storage allocated
- if (mNumProperties == mNumAllocated) {
- const unsigned int iOld = mNumAllocated;
- mNumAllocated *= 2;
- aiMaterialProperty **ppTemp;
- try {
- ppTemp = new aiMaterialProperty *[mNumAllocated];
- } catch (std::bad_alloc &) {
- delete pcNew;
- return AI_OUTOFMEMORY;
- }
- // just copy all items over; then replace the old array
- memcpy(ppTemp, mProperties, iOld * sizeof(void *));
- delete[] mProperties;
- mProperties = ppTemp;
- }
- // push back ...
- mProperties[mNumProperties++] = pcNew;
- return AI_SUCCESS;
- }
- // ------------------------------------------------------------------------------------------------
- aiReturn aiMaterial::AddProperty(const aiString *pInput,
- const char *pKey,
- unsigned int type,
- unsigned int index) {
- ai_assert(sizeof(ai_uint32) == 4);
- return AddBinaryProperty(pInput,
- static_cast<unsigned int>(pInput->length + 1 + 4),
- pKey,
- type,
- index,
- aiPTI_String);
- }
- // ------------------------------------------------------------------------------------------------
- uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName /*= false*/) {
- uint32_t hash = 1503; // magic start value, chosen to be my birthday :-)
- for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
- aiMaterialProperty *prop;
- // Exclude all properties whose first character is '?' from the hash
- // See doc for aiMaterialProperty.
- prop = mat->mProperties[i];
- if (nullptr != prop && (includeMatName || prop->mKey.data[0] != '?')) {
- hash = SuperFastHash(prop->mKey.data, (unsigned int)prop->mKey.length, hash);
- hash = SuperFastHash(prop->mData, prop->mDataLength, hash);
- // Combine the semantic and the index with the hash
- hash = SuperFastHash((const char *)&prop->mSemantic, sizeof(unsigned int), hash);
- hash = SuperFastHash((const char *)&prop->mIndex, sizeof(unsigned int), hash);
- }
- }
- return hash;
- }
- // ------------------------------------------------------------------------------------------------
- void aiMaterial::CopyPropertyList(aiMaterial *pcDest,
- const aiMaterial *pcSrc) {
- ai_assert(nullptr != pcDest);
- ai_assert(nullptr != pcSrc);
- unsigned int iOldNum = pcDest->mNumProperties;
- pcDest->mNumAllocated += pcSrc->mNumAllocated;
- pcDest->mNumProperties += pcSrc->mNumProperties;
- aiMaterialProperty **pcOld = pcDest->mProperties;
- pcDest->mProperties = new aiMaterialProperty *[pcDest->mNumAllocated];
- if (iOldNum && pcOld) {
- for (unsigned int i = 0; i < iOldNum; ++i) {
- pcDest->mProperties[i] = pcOld[i];
- }
- }
- if (pcOld) {
- delete[] pcOld;
- }
- for (unsigned int i = iOldNum; i < pcDest->mNumProperties; ++i) {
- aiMaterialProperty *propSrc = pcSrc->mProperties[i];
- // search whether we have already a property with this name -> if yes, overwrite it
- aiMaterialProperty *prop;
- for (unsigned int q = 0; q < iOldNum; ++q) {
- prop = pcDest->mProperties[q];
- if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic && prop->mIndex == propSrc->mIndex) {
- delete prop;
- // collapse the whole array ...
- memmove(&pcDest->mProperties[q], &pcDest->mProperties[q + 1], i - q);
- i--;
- pcDest->mNumProperties--;
- }
- }
- // Allocate the output property and copy the source property
- prop = pcDest->mProperties[i] = new aiMaterialProperty();
- prop->mKey = propSrc->mKey;
- prop->mDataLength = propSrc->mDataLength;
- prop->mType = propSrc->mType;
- prop->mSemantic = propSrc->mSemantic;
- prop->mIndex = propSrc->mIndex;
- prop->mData = new char[propSrc->mDataLength];
- memcpy(prop->mData, propSrc->mData, prop->mDataLength);
- }
- }
|