// // KRMaterialManager.cpp // Kraken Engine // // Copyright 2024 Kearwood Gilbert. All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. 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. // // THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''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 KEARWOOD GILBERT 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. // // The views and conclusions contained in the software and documentation are those of the // authors and should not be interpreted as representing official policies, either expressed // or implied, of Kearwood Gilbert. // #include "KREngine-common.h" #include "KRMaterialManager.h" using namespace mimir; using namespace hydra; KRMaterialManager::KRMaterialManager(KRContext& context, KRTextureManager* pTextureManager, KRPipelineManager* pPipelineManager) : KRResourceManager(context) { m_pTextureManager = pTextureManager; m_pPipelineManager = pPipelineManager; } KRMaterialManager::~KRMaterialManager() { } KRResource* KRMaterialManager::loadResource(const std::string& name, const std::string& extension, Block* data) { if (extension.compare("mtl") == 0) { return load(name.c_str(), data); } return nullptr; } KRResource* KRMaterialManager::getResource(const std::string& name, const std::string& extension) { if (extension.compare("mtl") == 0) { // TODO - This is not correct -- there are multiple materials emitted in a single mtl file. // We should treat "mtl" files as source files, managed by KRSource, which output // material resources when compiled. return m_materials[name]; } return nullptr; } unordered_map& KRMaterialManager::getMaterials() { return m_materials; } KRMaterial* KRMaterialManager::getMaterial(const std::string& name) { std::string lowerName = name; std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); unordered_map::iterator itr = m_materials.find(lowerName); if (itr == m_materials.end()) { KRContext::Log(KRContext::LOG_LEVEL_WARNING, "Material not found: %s", name.c_str()); // Not found return NULL; } else { return (*itr).second; } } void KRMaterialManager::add(KRMaterial* new_material) { // FINDME, TODO - Potential memory leak if multiple materials with the same name are added std::string lowerName = new_material->getName(); std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); m_materials[lowerName] = new_material; } KRMaterial* KRMaterialManager::load(const char* szName, Block* data) { KRMaterial* pMaterial = NULL; char szSymbol[16][256]; data->lock(); char* pScan = (char*)data->getStart(); char* pEnd = (char*)data->getEnd(); while (pScan < pEnd) { // Scan through whitespace while (pScan < pEnd && (*pScan == ' ' || *pScan == '\t' || *pScan == '\r' || *pScan == '\n')) { pScan++; } if (*pScan == '#') { // Line is a comment line // Scan to the end of the line while (pScan < pEnd && *pScan != '\r' && *pScan != '\n') { pScan++; } } else { int cSymbols = 0; while (pScan < pEnd && *pScan != '\n' && *pScan != '\r') { char* pDest = szSymbol[cSymbols++]; while (pScan < pEnd && *pScan != ' ' && *pScan != '\n' && *pScan != '\r') { if (*pScan >= 'A' && *pScan <= 'Z') { *pDest++ = *pScan++ + 'a' - 'A'; // convert to lower case for case sensitve comparison later } else { *pDest++ = *pScan++; } } *pDest = '\0'; // Scan through whitespace, but don't advance to next line while (pScan < pEnd && (*pScan == ' ' || *pScan == '\t')) { pScan++; } } if (cSymbols > 0) { if (strcmp(szSymbol[0], "newmtl") == 0 && cSymbols >= 2) { pMaterial = new KRMaterial(*m_pContext, szSymbol[1]); m_materials[szSymbol[1]] = pMaterial; } if (pMaterial != NULL) { if (strcmp(szSymbol[0], "alpha_mode") == 0) { if (cSymbols == 2) { if (strcmp(szSymbol[1], "test") == 0) { pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_TEST); } else if (strcmp(szSymbol[1], "blendoneside") == 0) { pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDONESIDE); } else if (strcmp(szSymbol[1], "blendtwoside") == 0) { pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_BLENDTWOSIDE); } else { pMaterial->setAlphaMode(KRMaterial::KRMATERIAL_ALPHA_MODE_OPAQUE); } } } else if (strcmp(szSymbol[0], "ka") == 0) { char* pScan2 = szSymbol[1]; float r = strtof(pScan2, &pScan2); if (cSymbols == 2) { pMaterial->setAmbient(Vector3::Create(r, r, r)); } else if (cSymbols == 4) { pScan2 = szSymbol[2]; float g = strtof(pScan2, &pScan2); pScan2 = szSymbol[3]; float b = strtof(pScan2, &pScan2); pMaterial->setAmbient(Vector3::Create(r, g, b)); } } else if (strcmp(szSymbol[0], "kd") == 0) { char* pScan2 = szSymbol[1]; float r = strtof(pScan2, &pScan2); if (cSymbols == 2) { pMaterial->setDiffuse(Vector3::Create(r, r, r)); } else if (cSymbols == 4) { pScan2 = szSymbol[2]; float g = strtof(pScan2, &pScan2); pScan2 = szSymbol[3]; float b = strtof(pScan2, &pScan2); pMaterial->setDiffuse(Vector3::Create(r, g, b)); } } else if (strcmp(szSymbol[0], "ks") == 0) { char* pScan2 = szSymbol[1]; float r = strtof(pScan2, &pScan2); if (cSymbols == 2) { pMaterial->setSpecular(Vector3::Create(r, r, r)); } else if (cSymbols == 4) { pScan2 = szSymbol[2]; float g = strtof(pScan2, &pScan2); pScan2 = szSymbol[3]; float b = strtof(pScan2, &pScan2); pMaterial->setSpecular(Vector3::Create(r, g, b)); } } else if (strcmp(szSymbol[0], "kr") == 0) { char* pScan2 = szSymbol[1]; float r = strtof(pScan2, &pScan2); if (cSymbols == 2) { pMaterial->setReflection(Vector3::Create(r, r, r)); } else if (cSymbols == 4) { pScan2 = szSymbol[2]; float g = strtof(pScan2, &pScan2); pScan2 = szSymbol[3]; float b = strtof(pScan2, &pScan2); pMaterial->setReflection(Vector3::Create(r, g, b)); } } else if (strcmp(szSymbol[0], "tr") == 0) { char* pScan2 = szSymbol[1]; float a = strtof(pScan2, &pScan2); pMaterial->setTransparency(a); } else if (strcmp(szSymbol[0], "ns") == 0) { char* pScan2 = szSymbol[1]; float a = strtof(pScan2, &pScan2); pMaterial->setShininess(a); } else if (strncmp(szSymbol[0], "map", 3) == 0) { // Truncate file extension char* pScan2 = szSymbol[1]; char* pLastPeriod = NULL; while (*pScan2 != '\0') { if (*pScan2 == '.') { pLastPeriod = pScan2; } pScan2++; } if (pLastPeriod) { *pLastPeriod = '\0'; } Vector2 texture_scale = Vector2::Create(1.0f, 1.0f); Vector2 texture_offset = Vector2::Create(0.0f, 0.0f); int iScanSymbol = 2; int iScaleParam = -1; int iOffsetParam = -1; while (iScanSymbol < cSymbols) { if (strcmp(szSymbol[iScanSymbol], "-s") == 0) { // Scale iScaleParam = 0; iOffsetParam = -1; } else if (strcmp(szSymbol[iScanSymbol], "-o") == 0) { // Offset iOffsetParam = 0; iScaleParam = -1; } else { char* pScan3 = szSymbol[iScanSymbol]; float v = strtof(pScan3, &pScan3); if (iScaleParam == 0) { texture_scale.x = v; iScaleParam++; } else if (iScaleParam == 1) { texture_scale.y = v; iScaleParam++; } else if (iOffsetParam == 0) { texture_offset.x = v; iOffsetParam++; } else if (iOffsetParam == 1) { texture_offset.y = v; iOffsetParam++; } } iScanSymbol++; } if (strcmp(szSymbol[0], "map_ka") == 0) { pMaterial->setAmbientMap(szSymbol[1], texture_scale, texture_offset); } else if (strcmp(szSymbol[0], "map_kd") == 0) { pMaterial->setDiffuseMap(szSymbol[1], texture_scale, texture_offset); } else if (strcmp(szSymbol[0], "map_ks") == 0) { pMaterial->setSpecularMap(szSymbol[1], texture_scale, texture_offset); } else if (strcmp(szSymbol[0], "map_normal") == 0) { pMaterial->setNormalMap(szSymbol[1], texture_scale, texture_offset); } else if (strcmp(szSymbol[0], "map_reflection") == 0) { pMaterial->setReflectionMap(szSymbol[1], texture_scale, texture_offset); } else if (strcmp(szSymbol[0], "map_reflectioncube") == 0) { pMaterial->setReflectionCube(szSymbol[1]); } } } } } } data->unlock(); delete data; return pMaterial; }