1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2019, assimp team
7
8
9
10 All rights reserved.
11
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15
16 * Redistributions of source code must retain the above
17 copyright notice, this list of conditions and the
18 following disclaimer.
19
20 * Redistributions in binary form must reproduce the above
21 copyright notice, this list of conditions and the
22 following disclaimer in the documentation and/or other
23 materials provided with the distribution.
24
25 * Neither the name of the assimp team, nor the names of its
26 contributors may be used to endorse or promote products
27 derived from this software without specific prior
28 written permission of the assimp team.
29
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43
44
45 #ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
46
47 #include <stdlib.h>
48 #include "ObjFileMtlImporter.h"
49 #include "ObjTools.h"
50 #include "ObjFileData.h"
51 #include <assimp/fast_atof.h>
52 #include <assimp/ParsingUtils.h>
53 #include <assimp/material.h>
54 #include <assimp/DefaultLogger.hpp>
55
56 namespace Assimp {
57
58 // Material specific token (case insensitive compare)
59 static const std::string DiffuseTexture = "map_Kd";
60 static const std::string AmbientTexture = "map_Ka";
61 static const std::string SpecularTexture = "map_Ks";
62 static const std::string OpacityTexture = "map_d";
63 static const std::string EmissiveTexture1 = "map_emissive";
64 static const std::string EmissiveTexture2 = "map_Ke";
65 static const std::string BumpTexture1 = "map_bump";
66 static const std::string BumpTexture2 = "bump";
67 static const std::string NormalTexture = "map_Kn";
68 static const std::string ReflectionTexture = "refl";
69 static const std::string DisplacementTexture1 = "map_disp";
70 static const std::string DisplacementTexture2 = "disp";
71 static const std::string SpecularityTexture = "map_ns";
72
73 // texture option specific token
74 static const std::string BlendUOption = "-blendu";
75 static const std::string BlendVOption = "-blendv";
76 static const std::string BoostOption = "-boost";
77 static const std::string ModifyMapOption = "-mm";
78 static const std::string OffsetOption = "-o";
79 static const std::string ScaleOption = "-s";
80 static const std::string TurbulenceOption = "-t";
81 static const std::string ResolutionOption = "-texres";
82 static const std::string ClampOption = "-clamp";
83 static const std::string BumpOption = "-bm";
84 static const std::string ChannelOption = "-imfchan";
85 static const std::string TypeOption = "-type";
86
87 // -------------------------------------------------------------------
88 // Constructor
ObjFileMtlImporter(std::vector<char> & buffer,const std::string &,ObjFile::Model * pModel)89 ObjFileMtlImporter::ObjFileMtlImporter( std::vector<char> &buffer,
90 const std::string &,
91 ObjFile::Model *pModel ) :
92 m_DataIt( buffer.begin() ),
93 m_DataItEnd( buffer.end() ),
94 m_pModel( pModel ),
95 m_uiLine( 0 )
96 {
97 ai_assert( NULL != m_pModel );
98 if ( NULL == m_pModel->m_pDefaultMaterial )
99 {
100 m_pModel->m_pDefaultMaterial = new ObjFile::Material;
101 m_pModel->m_pDefaultMaterial->MaterialName.Set( "default" );
102 }
103 load();
104 }
105
106 // -------------------------------------------------------------------
107 // Destructor
~ObjFileMtlImporter()108 ObjFileMtlImporter::~ObjFileMtlImporter()
109 {
110 // empty
111 }
112
113 // -------------------------------------------------------------------
114 // Private copy constructor
ObjFileMtlImporter(const ObjFileMtlImporter &)115 ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter & )
116 {
117 // empty
118 }
119
120 // -------------------------------------------------------------------
121 // Private copy constructor
operator =(const ObjFileMtlImporter &)122 ObjFileMtlImporter &ObjFileMtlImporter::operator = ( const ObjFileMtlImporter & )
123 {
124 return *this;
125 }
126
127 // -------------------------------------------------------------------
128 // Loads the material description
load()129 void ObjFileMtlImporter::load()
130 {
131 if ( m_DataIt == m_DataItEnd )
132 return;
133
134 while ( m_DataIt != m_DataItEnd )
135 {
136 switch (*m_DataIt)
137 {
138 case 'k':
139 case 'K':
140 {
141 ++m_DataIt;
142 if (*m_DataIt == 'a') // Ambient color
143 {
144 ++m_DataIt;
145 getColorRGBA( &m_pModel->m_pCurrentMaterial->ambient );
146 }
147 else if (*m_DataIt == 'd') // Diffuse color
148 {
149 ++m_DataIt;
150 getColorRGBA( &m_pModel->m_pCurrentMaterial->diffuse );
151 }
152 else if (*m_DataIt == 's')
153 {
154 ++m_DataIt;
155 getColorRGBA( &m_pModel->m_pCurrentMaterial->specular );
156 }
157 else if (*m_DataIt == 'e')
158 {
159 ++m_DataIt;
160 getColorRGBA( &m_pModel->m_pCurrentMaterial->emissive );
161 }
162 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
163 }
164 break;
165 case 'T':
166 {
167 ++m_DataIt;
168 if (*m_DataIt == 'f') // Material transmission
169 {
170 ++m_DataIt;
171 getColorRGBA( &m_pModel->m_pCurrentMaterial->transparent);
172 }
173 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
174 }
175 break;
176 case 'd':
177 {
178 if( *(m_DataIt+1) == 'i' && *( m_DataIt + 2 ) == 's' && *( m_DataIt + 3 ) == 'p' ) {
179 // A displacement map
180 getTexture();
181 } else {
182 // Alpha value
183 ++m_DataIt;
184 getFloatValue( m_pModel->m_pCurrentMaterial->alpha );
185 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
186 }
187 }
188 break;
189
190 case 'N':
191 case 'n':
192 {
193 ++m_DataIt;
194 switch(*m_DataIt)
195 {
196 case 's': // Specular exponent
197 ++m_DataIt;
198 getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
199 break;
200 case 'i': // Index Of refraction
201 ++m_DataIt;
202 getFloatValue(m_pModel->m_pCurrentMaterial->ior);
203 break;
204 case 'e': // New material
205 createMaterial();
206 break;
207 }
208 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
209 }
210 break;
211
212 case 'm': // Texture
213 case 'b': // quick'n'dirty - for 'bump' sections
214 case 'r': // quick'n'dirty - for 'refl' sections
215 {
216 getTexture();
217 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
218 }
219 break;
220
221 case 'i': // Illumination model
222 {
223 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
224 getIlluminationModel( m_pModel->m_pCurrentMaterial->illumination_model );
225 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
226 }
227 break;
228
229 default:
230 {
231 m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
232 }
233 break;
234 }
235 }
236 }
237
238 // -------------------------------------------------------------------
239 // Loads a color definition
getColorRGBA(aiColor3D * pColor)240 void ObjFileMtlImporter::getColorRGBA( aiColor3D *pColor )
241 {
242 ai_assert( NULL != pColor );
243
244 ai_real r( 0.0 ), g( 0.0 ), b( 0.0 );
245 m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, r );
246 pColor->r = r;
247
248 // we have to check if color is default 0 with only one token
249 if( !IsLineEnd( *m_DataIt ) ) {
250 m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, g );
251 m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, b );
252 }
253 pColor->g = g;
254 pColor->b = b;
255 }
256
257 // -------------------------------------------------------------------
258 // Loads the kind of illumination model.
getIlluminationModel(int & illum_model)259 void ObjFileMtlImporter::getIlluminationModel( int &illum_model )
260 {
261 m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE );
262 illum_model = atoi(m_buffer);
263 }
264
265 // -------------------------------------------------------------------
266 // Loads a single float value.
getFloatValue(ai_real & value)267 void ObjFileMtlImporter::getFloatValue( ai_real &value )
268 {
269 m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE );
270 value = (ai_real) fast_atof(m_buffer);
271 }
272
273 // -------------------------------------------------------------------
274 // Creates a material from loaded data.
createMaterial()275 void ObjFileMtlImporter::createMaterial()
276 {
277 std::string line( "" );
278 while( !IsLineEnd( *m_DataIt ) ) {
279 line += *m_DataIt;
280 ++m_DataIt;
281 }
282
283 std::vector<std::string> token;
284 const unsigned int numToken = tokenize<std::string>( line, token, " \t" );
285 std::string name( "" );
286 if ( numToken == 1 ) {
287 name = AI_DEFAULT_MATERIAL_NAME;
288 } else {
289 // skip newmtl and all following white spaces
290 std::size_t first_ws_pos = line.find_first_of(" \t");
291 std::size_t first_non_ws_pos = line.find_first_not_of(" \t", first_ws_pos);
292 if (first_non_ws_pos != std::string::npos) {
293 name = line.substr(first_non_ws_pos);
294 }
295 }
296
297 name = trim_whitespaces(name);
298
299 std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( name );
300 if ( m_pModel->m_MaterialMap.end() == it) {
301 // New Material created
302 m_pModel->m_pCurrentMaterial = new ObjFile::Material();
303 m_pModel->m_pCurrentMaterial->MaterialName.Set( name );
304 m_pModel->m_MaterialLib.push_back( name );
305 m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial;
306
307 if (m_pModel->m_pCurrentMesh) {
308 m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast<unsigned int>(m_pModel->m_MaterialLib.size() - 1);
309 }
310 } else {
311 // Use older material
312 m_pModel->m_pCurrentMaterial = (*it).second;
313 }
314 }
315
316 // -------------------------------------------------------------------
317 // Gets a texture name from data.
getTexture()318 void ObjFileMtlImporter::getTexture() {
319 aiString *out( NULL );
320 int clampIndex = -1;
321
322 const char *pPtr( &(*m_DataIt) );
323 if ( !ASSIMP_strincmp( pPtr, DiffuseTexture.c_str(), static_cast<unsigned int>(DiffuseTexture.size()) ) ) {
324 // Diffuse texture
325 out = & m_pModel->m_pCurrentMaterial->texture;
326 clampIndex = ObjFile::Material::TextureDiffuseType;
327 } else if ( !ASSIMP_strincmp( pPtr,AmbientTexture.c_str(), static_cast<unsigned int>(AmbientTexture.size()) ) ) {
328 // Ambient texture
329 out = & m_pModel->m_pCurrentMaterial->textureAmbient;
330 clampIndex = ObjFile::Material::TextureAmbientType;
331 } else if ( !ASSIMP_strincmp( pPtr, SpecularTexture.c_str(), static_cast<unsigned int>(SpecularTexture.size()) ) ) {
332 // Specular texture
333 out = & m_pModel->m_pCurrentMaterial->textureSpecular;
334 clampIndex = ObjFile::Material::TextureSpecularType;
335 } else if ( !ASSIMP_strincmp( pPtr, DisplacementTexture1.c_str(), static_cast<unsigned int>(DisplacementTexture1.size()) ) ||
336 !ASSIMP_strincmp( pPtr, DisplacementTexture2.c_str(), static_cast<unsigned int>(DisplacementTexture2.size()) ) ) {
337 // Displacement texture
338 out = &m_pModel->m_pCurrentMaterial->textureDisp;
339 clampIndex = ObjFile::Material::TextureDispType;
340 } else if ( !ASSIMP_strincmp( pPtr, OpacityTexture.c_str(), static_cast<unsigned int>(OpacityTexture.size()) ) ) {
341 // Opacity texture
342 out = & m_pModel->m_pCurrentMaterial->textureOpacity;
343 clampIndex = ObjFile::Material::TextureOpacityType;
344 } else if ( !ASSIMP_strincmp( pPtr, EmissiveTexture1.c_str(), static_cast<unsigned int>(EmissiveTexture1.size()) ) ||
345 !ASSIMP_strincmp( pPtr, EmissiveTexture2.c_str(), static_cast<unsigned int>(EmissiveTexture2.size()) ) ) {
346 // Emissive texture
347 out = & m_pModel->m_pCurrentMaterial->textureEmissive;
348 clampIndex = ObjFile::Material::TextureEmissiveType;
349 } else if ( !ASSIMP_strincmp( pPtr, BumpTexture1.c_str(), static_cast<unsigned int>(BumpTexture1.size()) ) ||
350 !ASSIMP_strincmp( pPtr, BumpTexture2.c_str(), static_cast<unsigned int>(BumpTexture2.size()) ) ) {
351 // Bump texture
352 out = & m_pModel->m_pCurrentMaterial->textureBump;
353 clampIndex = ObjFile::Material::TextureBumpType;
354 } else if ( !ASSIMP_strincmp( pPtr,NormalTexture.c_str(), static_cast<unsigned int>(NormalTexture.size()) ) ) {
355 // Normal map
356 out = & m_pModel->m_pCurrentMaterial->textureNormal;
357 clampIndex = ObjFile::Material::TextureNormalType;
358 } else if( !ASSIMP_strincmp( pPtr, ReflectionTexture.c_str(), static_cast<unsigned int>(ReflectionTexture.size()) ) ) {
359 // Reflection texture(s)
360 //Do nothing here
361 return;
362 } else if ( !ASSIMP_strincmp( pPtr, SpecularityTexture.c_str(), static_cast<unsigned int>(SpecularityTexture.size()) ) ) {
363 // Specularity scaling (glossiness)
364 out = & m_pModel->m_pCurrentMaterial->textureSpecularity;
365 clampIndex = ObjFile::Material::TextureSpecularityType;
366 } else {
367 ASSIMP_LOG_ERROR("OBJ/MTL: Encountered unknown texture type");
368 return;
369 }
370
371 bool clamp = false;
372 getTextureOption(clamp, clampIndex, out);
373 m_pModel->m_pCurrentMaterial->clamp[clampIndex] = clamp;
374
375 std::string texture;
376 m_DataIt = getName<DataArrayIt>( m_DataIt, m_DataItEnd, texture );
377 if ( NULL!=out ) {
378 out->Set( texture );
379 }
380 }
381
382 /* /////////////////////////////////////////////////////////////////////////////
383 * Texture Option
384 * /////////////////////////////////////////////////////////////////////////////
385 * According to http://en.wikipedia.org/wiki/Wavefront_.obj_file#Texture_options
386 * Texture map statement can contains various texture option, for example:
387 *
388 * map_Ka -o 1 1 1 some.png
389 * map_Kd -clamp on some.png
390 *
391 * So we need to parse and skip these options, and leave the last part which is
392 * the url of image, otherwise we will get a wrong url like "-clamp on some.png".
393 *
394 * Because aiMaterial supports clamp option, so we also want to return it
395 * /////////////////////////////////////////////////////////////////////////////
396 */
getTextureOption(bool & clamp,int & clampIndex,aiString * & out)397 void ObjFileMtlImporter::getTextureOption(bool &clamp, int &clampIndex, aiString *&out) {
398 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
399
400 // If there is any more texture option
401 while (!isEndOfBuffer(m_DataIt, m_DataItEnd) && *m_DataIt == '-')
402 {
403 const char *pPtr( &(*m_DataIt) );
404 //skip option key and value
405 int skipToken = 1;
406
407 if (!ASSIMP_strincmp(pPtr, ClampOption.c_str(), static_cast<unsigned int>(ClampOption.size())))
408 {
409 DataArrayIt it = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
410 char value[3];
411 CopyNextWord(it, m_DataItEnd, value, sizeof(value) / sizeof(*value));
412 if (!ASSIMP_strincmp(value, "on", 2))
413 {
414 clamp = true;
415 }
416
417 skipToken = 2;
418 }
419 else if( !ASSIMP_strincmp( pPtr, TypeOption.c_str(), static_cast<unsigned int>(TypeOption.size()) ) )
420 {
421 DataArrayIt it = getNextToken<DataArrayIt>( m_DataIt, m_DataItEnd );
422 char value[ 12 ];
423 CopyNextWord( it, m_DataItEnd, value, sizeof( value ) / sizeof( *value ) );
424 if( !ASSIMP_strincmp( value, "cube_top", 8 ) )
425 {
426 clampIndex = ObjFile::Material::TextureReflectionCubeTopType;
427 out = &m_pModel->m_pCurrentMaterial->textureReflection[0];
428 }
429 else if( !ASSIMP_strincmp( value, "cube_bottom", 11 ) )
430 {
431 clampIndex = ObjFile::Material::TextureReflectionCubeBottomType;
432 out = &m_pModel->m_pCurrentMaterial->textureReflection[1];
433 }
434 else if( !ASSIMP_strincmp( value, "cube_front", 10 ) )
435 {
436 clampIndex = ObjFile::Material::TextureReflectionCubeFrontType;
437 out = &m_pModel->m_pCurrentMaterial->textureReflection[2];
438 }
439 else if( !ASSIMP_strincmp( value, "cube_back", 9 ) )
440 {
441 clampIndex = ObjFile::Material::TextureReflectionCubeBackType;
442 out = &m_pModel->m_pCurrentMaterial->textureReflection[3];
443 }
444 else if( !ASSIMP_strincmp( value, "cube_left", 9 ) )
445 {
446 clampIndex = ObjFile::Material::TextureReflectionCubeLeftType;
447 out = &m_pModel->m_pCurrentMaterial->textureReflection[4];
448 }
449 else if( !ASSIMP_strincmp( value, "cube_right", 10 ) )
450 {
451 clampIndex = ObjFile::Material::TextureReflectionCubeRightType;
452 out = &m_pModel->m_pCurrentMaterial->textureReflection[5];
453 }
454 else if( !ASSIMP_strincmp( value, "sphere", 6 ) )
455 {
456 clampIndex = ObjFile::Material::TextureReflectionSphereType;
457 out = &m_pModel->m_pCurrentMaterial->textureReflection[0];
458 }
459
460 skipToken = 2;
461 }
462 else if (!ASSIMP_strincmp(pPtr, BlendUOption.c_str(), static_cast<unsigned int>(BlendUOption.size()))
463 || !ASSIMP_strincmp(pPtr, BlendVOption.c_str(), static_cast<unsigned int>(BlendVOption.size()))
464 || !ASSIMP_strincmp(pPtr, BoostOption.c_str(), static_cast<unsigned int>(BoostOption.size()))
465 || !ASSIMP_strincmp(pPtr, ResolutionOption.c_str(), static_cast<unsigned int>(ResolutionOption.size()))
466 || !ASSIMP_strincmp(pPtr, BumpOption.c_str(), static_cast<unsigned int>(BumpOption.size()))
467 || !ASSIMP_strincmp(pPtr, ChannelOption.c_str(), static_cast<unsigned int>(ChannelOption.size())))
468 {
469 skipToken = 2;
470 }
471 else if (!ASSIMP_strincmp(pPtr, ModifyMapOption.c_str(), static_cast<unsigned int>(ModifyMapOption.size())))
472 {
473 skipToken = 3;
474 }
475 else if ( !ASSIMP_strincmp(pPtr, OffsetOption.c_str(), static_cast<unsigned int>(OffsetOption.size()))
476 || !ASSIMP_strincmp(pPtr, ScaleOption.c_str(), static_cast<unsigned int>(ScaleOption.size()))
477 || !ASSIMP_strincmp(pPtr, TurbulenceOption.c_str(), static_cast<unsigned int>(TurbulenceOption.size()))
478 )
479 {
480 skipToken = 4;
481 }
482
483 for (int i = 0; i < skipToken; ++i)
484 {
485 m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
486 }
487 }
488 }
489
490 // -------------------------------------------------------------------
491
492 } // Namespace Assimp
493
494 #endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
495