| /* |
| --------------------------------------------------------------------------- |
| Open Asset Import Library (assimp) |
| --------------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, 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. |
| --------------------------------------------------------------------------- |
| */ |
| |
| |
| #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER |
| |
| #include <stdlib.h> |
| #include "ObjFileMtlImporter.h" |
| #include "ObjTools.h" |
| #include "ObjFileData.h" |
| #include "fast_atof.h" |
| #include "ParsingUtils.h" |
| #include <assimp/material.h> |
| #include <assimp/DefaultLogger.hpp> |
| |
| |
| namespace Assimp { |
| |
| // Material specific token (case insensitive compare) |
| static const std::string DiffuseTexture = "map_Kd"; |
| static const std::string AmbientTexture = "map_Ka"; |
| static const std::string SpecularTexture = "map_Ks"; |
| static const std::string OpacityTexture = "map_d"; |
| static const std::string EmissiveTexture1 = "map_emissive"; |
| static const std::string EmissiveTexture2 = "map_Ke"; |
| static const std::string BumpTexture1 = "map_bump"; |
| static const std::string BumpTexture2 = "bump"; |
| static const std::string NormalTexture = "map_Kn"; |
| static const std::string ReflectionTexture = "refl"; |
| static const std::string DisplacementTexture1 = "map_disp"; |
| static const std::string DisplacementTexture2 = "disp"; |
| static const std::string SpecularityTexture = "map_ns"; |
| |
| // texture option specific token |
| static const std::string BlendUOption = "-blendu"; |
| static const std::string BlendVOption = "-blendv"; |
| static const std::string BoostOption = "-boost"; |
| static const std::string ModifyMapOption = "-mm"; |
| static const std::string OffsetOption = "-o"; |
| static const std::string ScaleOption = "-s"; |
| static const std::string TurbulenceOption = "-t"; |
| static const std::string ResolutionOption = "-texres"; |
| static const std::string ClampOption = "-clamp"; |
| static const std::string BumpOption = "-bm"; |
| static const std::string ChannelOption = "-imfchan"; |
| static const std::string TypeOption = "-type"; |
| |
| |
| |
| // ------------------------------------------------------------------- |
| // Constructor |
| ObjFileMtlImporter::ObjFileMtlImporter( std::vector<char> &buffer, |
| const std::string &, |
| ObjFile::Model *pModel ) : |
| m_DataIt( buffer.begin() ), |
| m_DataItEnd( buffer.end() ), |
| m_pModel( pModel ), |
| m_uiLine( 0 ) |
| { |
| ai_assert( NULL != m_pModel ); |
| if ( NULL == m_pModel->m_pDefaultMaterial ) |
| { |
| m_pModel->m_pDefaultMaterial = new ObjFile::Material; |
| m_pModel->m_pDefaultMaterial->MaterialName.Set( "default" ); |
| } |
| load(); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Destructor |
| ObjFileMtlImporter::~ObjFileMtlImporter() |
| { |
| // empty |
| } |
| |
| // ------------------------------------------------------------------- |
| // Private copy constructor |
| ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter & ) |
| { |
| // empty |
| } |
| |
| // ------------------------------------------------------------------- |
| // Private copy constructor |
| ObjFileMtlImporter &ObjFileMtlImporter::operator = ( const ObjFileMtlImporter & ) |
| { |
| return *this; |
| } |
| |
| // ------------------------------------------------------------------- |
| // Loads the material description |
| void ObjFileMtlImporter::load() |
| { |
| if ( m_DataIt == m_DataItEnd ) |
| return; |
| |
| while ( m_DataIt != m_DataItEnd ) |
| { |
| switch (*m_DataIt) |
| { |
| case 'k': |
| case 'K': |
| { |
| ++m_DataIt; |
| if (*m_DataIt == 'a') // Ambient color |
| { |
| ++m_DataIt; |
| getColorRGBA( &m_pModel->m_pCurrentMaterial->ambient ); |
| } |
| else if (*m_DataIt == 'd') // Diffuse color |
| { |
| ++m_DataIt; |
| getColorRGBA( &m_pModel->m_pCurrentMaterial->diffuse ); |
| } |
| else if (*m_DataIt == 's') |
| { |
| ++m_DataIt; |
| getColorRGBA( &m_pModel->m_pCurrentMaterial->specular ); |
| } |
| else if (*m_DataIt == 'e') |
| { |
| ++m_DataIt; |
| getColorRGBA( &m_pModel->m_pCurrentMaterial->emissive ); |
| } |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| case 'T': |
| { |
| ++m_DataIt; |
| if (*m_DataIt == 'f') // Material transmission |
| { |
| ++m_DataIt; |
| getColorRGBA( &m_pModel->m_pCurrentMaterial->transparent); |
| } |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| case 'd': |
| { |
| if( *(m_DataIt+1) == 'i' && *( m_DataIt + 2 ) == 's' && *( m_DataIt + 3 ) == 'p' ) { |
| // A displacement map |
| getTexture(); |
| } else { |
| // Alpha value |
| ++m_DataIt; |
| getFloatValue( m_pModel->m_pCurrentMaterial->alpha ); |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| } |
| break; |
| |
| case 'N': |
| case 'n': |
| { |
| ++m_DataIt; |
| switch(*m_DataIt) |
| { |
| case 's': // Specular exponent |
| ++m_DataIt; |
| getFloatValue(m_pModel->m_pCurrentMaterial->shineness); |
| break; |
| case 'i': // Index Of refraction |
| ++m_DataIt; |
| getFloatValue(m_pModel->m_pCurrentMaterial->ior); |
| break; |
| case 'e': // New material |
| createMaterial(); |
| break; |
| } |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| |
| case 'm': // Texture |
| case 'b': // quick'n'dirty - for 'bump' sections |
| case 'r': // quick'n'dirty - for 'refl' sections |
| { |
| getTexture(); |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| |
| case 'i': // Illumination model |
| { |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| getIlluminationModel( m_pModel->m_pCurrentMaterial->illumination_model ); |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| |
| default: |
| { |
| m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine ); |
| } |
| break; |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| // Loads a color definition |
| void ObjFileMtlImporter::getColorRGBA( aiColor3D *pColor ) |
| { |
| ai_assert( NULL != pColor ); |
| |
| ai_real r( 0.0 ), g( 0.0 ), b( 0.0 ); |
| m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, r ); |
| pColor->r = r; |
| |
| // we have to check if color is default 0 with only one token |
| if( !IsLineEnd( *m_DataIt ) ) { |
| m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, g ); |
| m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, b ); |
| } |
| pColor->g = g; |
| pColor->b = b; |
| } |
| |
| // ------------------------------------------------------------------- |
| // Loads the kind of illumination model. |
| void ObjFileMtlImporter::getIlluminationModel( int &illum_model ) |
| { |
| m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE ); |
| illum_model = atoi(m_buffer); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Loads a single float value. |
| void ObjFileMtlImporter::getFloatValue( ai_real &value ) |
| { |
| m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE ); |
| value = (ai_real) fast_atof(m_buffer); |
| } |
| |
| // ------------------------------------------------------------------- |
| // Creates a material from loaded data. |
| void ObjFileMtlImporter::createMaterial() |
| { |
| std::string line( "" ); |
| while( !IsLineEnd( *m_DataIt ) ) { |
| line += *m_DataIt; |
| ++m_DataIt; |
| } |
| |
| std::vector<std::string> token; |
| const unsigned int numToken = tokenize<std::string>( line, token, " \t" ); |
| std::string name( "" ); |
| if ( numToken == 1 ) { |
| name = AI_DEFAULT_MATERIAL_NAME; |
| } else { |
| // skip newmtl and all following white spaces |
| std::size_t first_ws_pos = line.find_first_of(" \t"); |
| std::size_t first_non_ws_pos = line.find_first_not_of(" \t", first_ws_pos); |
| if (first_non_ws_pos != std::string::npos) { |
| name = line.substr(first_non_ws_pos); |
| } |
| } |
| |
| name = trim_whitespaces(name); |
| |
| std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( name ); |
| if ( m_pModel->m_MaterialMap.end() == it) { |
| // New Material created |
| m_pModel->m_pCurrentMaterial = new ObjFile::Material(); |
| m_pModel->m_pCurrentMaterial->MaterialName.Set( name ); |
| if (m_pModel->m_pCurrentMesh) { |
| m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast<unsigned int>(m_pModel->m_MaterialLib.size() - 1); |
| } |
| m_pModel->m_MaterialLib.push_back( name ); |
| m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial; |
| } else { |
| // Use older material |
| m_pModel->m_pCurrentMaterial = (*it).second; |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| // Gets a texture name from data. |
| void ObjFileMtlImporter::getTexture() { |
| aiString *out( NULL ); |
| int clampIndex = -1; |
| |
| const char *pPtr( &(*m_DataIt) ); |
| if ( !ASSIMP_strincmp( pPtr, DiffuseTexture.c_str(), static_cast<unsigned int>(DiffuseTexture.size()) ) ) { |
| // Diffuse texture |
| out = & m_pModel->m_pCurrentMaterial->texture; |
| clampIndex = ObjFile::Material::TextureDiffuseType; |
| } else if ( !ASSIMP_strincmp( pPtr,AmbientTexture.c_str(), static_cast<unsigned int>(AmbientTexture.size()) ) ) { |
| // Ambient texture |
| out = & m_pModel->m_pCurrentMaterial->textureAmbient; |
| clampIndex = ObjFile::Material::TextureAmbientType; |
| } else if ( !ASSIMP_strincmp( pPtr, SpecularTexture.c_str(), static_cast<unsigned int>(SpecularTexture.size()) ) ) { |
| // Specular texture |
| out = & m_pModel->m_pCurrentMaterial->textureSpecular; |
| clampIndex = ObjFile::Material::TextureSpecularType; |
| } else if ( !ASSIMP_strincmp( pPtr, OpacityTexture.c_str(), static_cast<unsigned int>(OpacityTexture.size()) ) ) { |
| // Opacity texture |
| out = & m_pModel->m_pCurrentMaterial->textureOpacity; |
| clampIndex = ObjFile::Material::TextureOpacityType; |
| } else if ( !ASSIMP_strincmp( pPtr, EmissiveTexture1.c_str(), static_cast<unsigned int>(EmissiveTexture1.size()) ) || |
| !ASSIMP_strincmp( pPtr, EmissiveTexture2.c_str(), static_cast<unsigned int>(EmissiveTexture2.size()) ) ) { |
| // Emissive texture |
| out = & m_pModel->m_pCurrentMaterial->textureEmissive; |
| clampIndex = ObjFile::Material::TextureEmissiveType; |
| } else if ( !ASSIMP_strincmp( pPtr, BumpTexture1.c_str(), static_cast<unsigned int>(BumpTexture1.size()) ) || |
| !ASSIMP_strincmp( pPtr, BumpTexture2.c_str(), static_cast<unsigned int>(BumpTexture2.size()) ) ) { |
| // Bump texture |
| out = & m_pModel->m_pCurrentMaterial->textureBump; |
| clampIndex = ObjFile::Material::TextureBumpType; |
| } else if ( !ASSIMP_strincmp( pPtr,NormalTexture.c_str(), static_cast<unsigned int>(NormalTexture.size()) ) ) { |
| // Normal map |
| out = & m_pModel->m_pCurrentMaterial->textureNormal; |
| clampIndex = ObjFile::Material::TextureNormalType; |
| } else if( !ASSIMP_strincmp( pPtr, ReflectionTexture.c_str(), static_cast<unsigned int>(ReflectionTexture.size()) ) ) { |
| // Reflection texture(s) |
| //Do nothing here |
| return; |
| } else if ( !ASSIMP_strincmp( pPtr, DisplacementTexture1.c_str(), static_cast<unsigned int>(DisplacementTexture1.size()) ) || |
| !ASSIMP_strincmp( pPtr, DisplacementTexture2.c_str(), static_cast<unsigned int>(DisplacementTexture2.size()) ) ) { |
| // Displacement texture |
| out = &m_pModel->m_pCurrentMaterial->textureDisp; |
| clampIndex = ObjFile::Material::TextureDispType; |
| } else if ( !ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()) ) ) { |
| // Specularity scaling (glossiness) |
| out = & m_pModel->m_pCurrentMaterial->textureSpecularity; |
| clampIndex = ObjFile::Material::TextureSpecularityType; |
| } else { |
| DefaultLogger::get()->error("OBJ/MTL: Encountered unknown texture type"); |
| return; |
| } |
| |
| bool clamp = false; |
| getTextureOption(clamp, clampIndex, out); |
| m_pModel->m_pCurrentMaterial->clamp[clampIndex] = clamp; |
| |
| std::string texture; |
| m_DataIt = getName<DataArrayIt>( m_DataIt, m_DataItEnd, texture ); |
| if ( NULL!=out ) { |
| out->Set( texture ); |
| } |
| } |
| |
| /* ///////////////////////////////////////////////////////////////////////////// |
| * Texture Option |
| * ///////////////////////////////////////////////////////////////////////////// |
| * According to http://en.wikipedia.org/wiki/Wavefront_.obj_file#Texture_options |
| * Texture map statement can contains various texture option, for example: |
| * |
| * map_Ka -o 1 1 1 some.png |
| * map_Kd -clamp on some.png |
| * |
| * So we need to parse and skip these options, and leave the last part which is |
| * the url of image, otherwise we will get a wrong url like "-clamp on some.png". |
| * |
| * Because aiMaterial supports clamp option, so we also want to return it |
| * ///////////////////////////////////////////////////////////////////////////// |
| */ |
| void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString *&out) { |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| |
| // If there is any more texture option |
| while (!isEndOfBuffer(m_DataIt, m_DataItEnd) && *m_DataIt == '-') |
| { |
| const char *pPtr( &(*m_DataIt) ); |
| //skip option key and value |
| int skipToken = 1; |
| |
| if (!ASSIMP_strincmp(pPtr, ClampOption.c_str(), static_cast<unsigned int>(ClampOption.size()))) |
| { |
| DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| char value[3]; |
| CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value)); |
| if (!ASSIMP_strincmp(value, "on", 2)) |
| { |
| clamp = true; |
| } |
| |
| skipToken = 2; |
| } |
| else if( !ASSIMP_strincmp( pPtr, TypeOption.c_str(), static_cast<unsigned int>(TypeOption.size()) ) ) |
| { |
| DataArrayIt it = getNextToken<DataArrayIt>( m_DataIt, m_DataItEnd ); |
| char value[ 12 ]; |
| CopyNextWord( it, m_DataItEnd, value, sizeof( value ) / sizeof( *value ) ); |
| if( !ASSIMP_strincmp( value, "cube_top", 8 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionCubeTopType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[0]; |
| } |
| else if( !ASSIMP_strincmp( value, "cube_bottom", 11 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionCubeBottomType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[1]; |
| } |
| else if( !ASSIMP_strincmp( value, "cube_front", 10 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionCubeFrontType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[2]; |
| } |
| else if( !ASSIMP_strincmp( value, "cube_back", 9 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionCubeBackType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[3]; |
| } |
| else if( !ASSIMP_strincmp( value, "cube_left", 9 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionCubeLeftType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[4]; |
| } |
| else if( !ASSIMP_strincmp( value, "cube_right", 10 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionCubeRightType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[5]; |
| } |
| else if( !ASSIMP_strincmp( value, "sphere", 6 ) ) |
| { |
| clampIndex = ObjFile::Material::TextureReflectionSphereType; |
| out = &m_pModel->m_pCurrentMaterial->textureReflection[0]; |
| } |
| |
| skipToken = 2; |
| } |
| else if (!ASSIMP_strincmp(pPtr, BlendUOption.c_str(), static_cast<unsigned int>(BlendUOption.size())) |
| || !ASSIMP_strincmp(pPtr, BlendVOption.c_str(), static_cast<unsigned int>(BlendVOption.size())) |
| || !ASSIMP_strincmp(pPtr, BoostOption.c_str(), static_cast<unsigned int>(BoostOption.size())) |
| || !ASSIMP_strincmp(pPtr, ResolutionOption.c_str(), static_cast<unsigned int>(ResolutionOption.size())) |
| || !ASSIMP_strincmp(pPtr, BumpOption.c_str(), static_cast<unsigned int>(BumpOption.size())) |
| || !ASSIMP_strincmp(pPtr, ChannelOption.c_str(), static_cast<unsigned int>(ChannelOption.size()))) |
| { |
| skipToken = 2; |
| } |
| else if (!ASSIMP_strincmp(pPtr, ModifyMapOption.c_str(), static_cast<unsigned int>(ModifyMapOption.size()))) |
| { |
| skipToken = 3; |
| } |
| else if ( !ASSIMP_strincmp(pPtr, OffsetOption.c_str(), static_cast<unsigned int>(OffsetOption.size())) |
| || !ASSIMP_strincmp(pPtr, ScaleOption.c_str(), static_cast<unsigned int>(ScaleOption.size())) |
| || !ASSIMP_strincmp(pPtr, TurbulenceOption.c_str(), static_cast<unsigned int>(TurbulenceOption.size())) |
| ) |
| { |
| skipToken = 4; |
| } |
| |
| for (int i = 0; i < skipToken; ++i) |
| { |
| m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd); |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------- |
| |
| } // Namespace Assimp |
| |
| #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER |