1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16   copyright notice, this list of conditions and the
17   following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20   copyright notice, this list of conditions and the
21   following disclaimer in the documentation and/or other
22   materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25   contributors may be used to endorse or promote products
26   derived from this software without specific prior
27   written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 ---------------------------------------------------------------------------
41 */
42 
43 /** @file  IRRShared.cpp
44  *  @brief Shared utilities for the IRR and IRRMESH loaders
45  */
46 
47 
48 
49 //This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted.
50 #if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
51 
52 #include "IRRShared.h"
53 #include "ParsingUtils.h"
54 #include "fast_atof.h"
55 #include <assimp/DefaultLogger.hpp>
56 #include <assimp/material.h>
57 
58 
59 using namespace Assimp;
60 using namespace irr;
61 using namespace irr::io;
62 
63 // Transformation matrix to convert from Assimp to IRR space
64 const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
65     1.0f, 0.0f, 0.0f, 0.0f,
66     0.0f, 0.0f, 1.0f, 0.0f,
67     0.0f, 1.0f, 0.0f, 0.0f,
68     0.0f, 0.0f, 0.0f, 1.0f);
69 
70 // ------------------------------------------------------------------------------------------------
71 // read a property in hexadecimal format (i.e. ffffffff)
ReadHexProperty(HexProperty & out)72 void IrrlichtBase::ReadHexProperty    (HexProperty&    out)
73 {
74     for (int i = 0; i < reader->getAttributeCount();++i)
75     {
76         if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
77         {
78             out.name = std::string( reader->getAttributeValue(i) );
79         }
80         else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
81         {
82             // parse the hexadecimal value
83             out.value = strtoul16(reader->getAttributeValue(i));
84         }
85     }
86 }
87 
88 // ------------------------------------------------------------------------------------------------
89 // read a decimal property
ReadIntProperty(IntProperty & out)90 void IrrlichtBase::ReadIntProperty    (IntProperty&    out)
91 {
92     for (int i = 0; i < reader->getAttributeCount();++i)
93     {
94         if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
95         {
96             out.name = std::string( reader->getAttributeValue(i) );
97         }
98         else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
99         {
100             // parse the ecimal value
101             out.value = strtol10(reader->getAttributeValue(i));
102         }
103     }
104 }
105 
106 // ------------------------------------------------------------------------------------------------
107 // read a string property
ReadStringProperty(StringProperty & out)108 void IrrlichtBase::ReadStringProperty (StringProperty& out)
109 {
110     for (int i = 0; i < reader->getAttributeCount();++i)
111     {
112         if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
113         {
114             out.name = std::string( reader->getAttributeValue(i) );
115         }
116         else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
117         {
118             // simple copy the string
119             out.value = std::string (reader->getAttributeValue(i));
120         }
121     }
122 }
123 
124 // ------------------------------------------------------------------------------------------------
125 // read a boolean property
ReadBoolProperty(BoolProperty & out)126 void IrrlichtBase::ReadBoolProperty   (BoolProperty&   out)
127 {
128     for (int i = 0; i < reader->getAttributeCount();++i)
129     {
130         if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
131         {
132             out.name = std::string( reader->getAttributeValue(i) );
133         }
134         else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
135         {
136             // true or false, case insensitive
137             out.value = (ASSIMP_stricmp( reader->getAttributeValue(i),
138                 "true") ? false : true);
139         }
140     }
141 }
142 
143 // ------------------------------------------------------------------------------------------------
144 // read a float property
ReadFloatProperty(FloatProperty & out)145 void IrrlichtBase::ReadFloatProperty  (FloatProperty&  out)
146 {
147     for (int i = 0; i < reader->getAttributeCount();++i)
148     {
149         if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
150         {
151             out.name = std::string( reader->getAttributeValue(i) );
152         }
153         else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
154         {
155             // just parse the float
156             out.value = fast_atof( reader->getAttributeValue(i) );
157         }
158     }
159 }
160 
161 // ------------------------------------------------------------------------------------------------
162 // read a vector property
ReadVectorProperty(VectorProperty & out)163 void IrrlichtBase::ReadVectorProperty  (VectorProperty&  out)
164 {
165     for (int i = 0; i < reader->getAttributeCount();++i)
166     {
167         if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
168         {
169             out.name = std::string( reader->getAttributeValue(i) );
170         }
171         else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
172         {
173             // three floats, separated with commas
174             const char* ptr = reader->getAttributeValue(i);
175 
176             SkipSpaces(&ptr);
177             ptr = fast_atoreal_move<float>( ptr,(float&)out.value.x );
178             SkipSpaces(&ptr);
179             if (',' != *ptr)
180             {
181                 DefaultLogger::get()->error("IRR(MESH): Expected comma in vector definition");
182             }
183             else SkipSpaces(ptr+1,&ptr);
184             ptr = fast_atoreal_move<float>( ptr,(float&)out.value.y );
185             SkipSpaces(&ptr);
186             if (',' != *ptr)
187             {
188                 DefaultLogger::get()->error("IRR(MESH): Expected comma in vector definition");
189             }
190             else SkipSpaces(ptr+1,&ptr);
191             ptr = fast_atoreal_move<float>( ptr,(float&)out.value.z );
192         }
193     }
194 }
195 
196 // ------------------------------------------------------------------------------------------------
197 // Convert a string to a proper aiMappingMode
ConvertMappingMode(const std::string & mode)198 int ConvertMappingMode(const std::string& mode)
199 {
200     if (mode == "texture_clamp_repeat")
201     {
202         return aiTextureMapMode_Wrap;
203     }
204     else if (mode == "texture_clamp_mirror")
205         return aiTextureMapMode_Mirror;
206 
207     return aiTextureMapMode_Clamp;
208 }
209 
210 // ------------------------------------------------------------------------------------------------
211 // Parse a material from the XML file
ParseMaterial(unsigned int & matFlags)212 aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
213 {
214     aiMaterial* mat = new aiMaterial();
215     aiColor4D clr;
216     aiString s;
217 
218     matFlags = 0; // zero output flags
219     int cnt  = 0; // number of used texture channels
220     unsigned int nd = 0;
221 
222     // Continue reading from the file
223     while (reader->read())
224     {
225         switch (reader->getNodeType())
226         {
227         case EXN_ELEMENT:
228 
229             // Hex properties
230             if (!ASSIMP_stricmp(reader->getNodeName(),"color"))
231             {
232                 HexProperty prop;
233                 ReadHexProperty(prop);
234                 if (prop.name == "Diffuse")
235                 {
236                     ColorFromARGBPacked(prop.value,clr);
237                     mat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
238                 }
239                 else if (prop.name == "Ambient")
240                 {
241                     ColorFromARGBPacked(prop.value,clr);
242                     mat->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
243                 }
244                 else if (prop.name == "Specular")
245                 {
246                     ColorFromARGBPacked(prop.value,clr);
247                     mat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
248                 }
249 
250                 // NOTE: The 'emissive' property causes problems. It is
251                 // often != 0, even if there is obviously no light
252                 // emitted by the described surface. In fact I think
253                 // IRRLICHT ignores this property, too.
254 #if 0
255                 else if (prop.name == "Emissive")
256                 {
257                     ColorFromARGBPacked(prop.value,clr);
258                     mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
259                 }
260 #endif
261             }
262             // Float properties
263             else if (!ASSIMP_stricmp(reader->getNodeName(),"float"))
264             {
265                 FloatProperty prop;
266                 ReadFloatProperty(prop);
267                 if (prop.name == "Shininess")
268                 {
269                     mat->AddProperty(&prop.value,1,AI_MATKEY_SHININESS);
270                 }
271             }
272             // Bool properties
273             else if (!ASSIMP_stricmp(reader->getNodeName(),"bool"))
274             {
275                 BoolProperty prop;
276                 ReadBoolProperty(prop);
277                 if (prop.name == "Wireframe")
278                 {
279                     int val = (prop.value ? true : false);
280                     mat->AddProperty(&val,1,AI_MATKEY_ENABLE_WIREFRAME);
281                 }
282                 else if (prop.name == "GouraudShading")
283                 {
284                     int val = (prop.value ? aiShadingMode_Gouraud
285                         : aiShadingMode_NoShading);
286                     mat->AddProperty(&val,1,AI_MATKEY_SHADING_MODEL);
287                 }
288                 else if (prop.name == "BackfaceCulling")
289                 {
290                     int val = (!prop.value);
291                     mat->AddProperty(&val,1,AI_MATKEY_TWOSIDED);
292                 }
293             }
294             // String properties - textures and texture related properties
295             else if (!ASSIMP_stricmp(reader->getNodeName(),"texture") ||
296                      !ASSIMP_stricmp(reader->getNodeName(),"enum"))
297             {
298                 StringProperty prop;
299                 ReadStringProperty(prop);
300                 if (prop.value.length())
301                 {
302                     // material type (shader)
303                     if (prop.name == "Type")
304                     {
305                         if (prop.value == "solid")
306                         {
307                             // default material ...
308                         }
309                         else if (prop.value == "trans_vertex_alpha")
310                         {
311                             matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
312                         }
313                         else if (prop.value == "lightmap")
314                         {
315                             matFlags = AI_IRRMESH_MAT_lightmap;
316                         }
317                         else if (prop.value == "solid_2layer")
318                         {
319                             matFlags = AI_IRRMESH_MAT_solid_2layer;
320                         }
321                         else if (prop.value == "lightmap_m2")
322                         {
323                             matFlags = AI_IRRMESH_MAT_lightmap_m2;
324                         }
325                         else if (prop.value == "lightmap_m4")
326                         {
327                             matFlags = AI_IRRMESH_MAT_lightmap_m4;
328                         }
329                         else if (prop.value == "lightmap_light")
330                         {
331                             matFlags = AI_IRRMESH_MAT_lightmap_light;
332                         }
333                         else if (prop.value == "lightmap_light_m2")
334                         {
335                             matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
336                         }
337                         else if (prop.value == "lightmap_light_m4")
338                         {
339                             matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
340                         }
341                         else if (prop.value == "lightmap_add")
342                         {
343                             matFlags = AI_IRRMESH_MAT_lightmap_add;
344                         }
345                         // Normal and parallax maps are treated equally
346                         else if (prop.value == "normalmap_solid" ||
347                             prop.value == "parallaxmap_solid")
348                         {
349                             matFlags = AI_IRRMESH_MAT_normalmap_solid;
350                         }
351                         else if (prop.value == "normalmap_trans_vertex_alpha" ||
352                             prop.value == "parallaxmap_trans_vertex_alpha")
353                         {
354                             matFlags = AI_IRRMESH_MAT_normalmap_tva;
355                         }
356                         else if (prop.value == "normalmap_trans_add" ||
357                             prop.value == "parallaxmap_trans_add")
358                         {
359                             matFlags = AI_IRRMESH_MAT_normalmap_ta;
360                         }
361                         else {
362                             DefaultLogger::get()->warn("IRRMat: Unrecognized material type: " + prop.value);
363                         }
364                     }
365 
366                     // Up to 4 texture channels are supported
367                     if (prop.name == "Texture1")
368                     {
369                         // Always accept the primary texture channel
370                         ++cnt;
371                         s.Set(prop.value);
372                         mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
373                     }
374                     else if (prop.name == "Texture2" && cnt == 1)
375                     {
376                         // 2-layer material lightmapped?
377                         if (matFlags & AI_IRRMESH_MAT_lightmap) {
378                             ++cnt;
379                             s.Set(prop.value);
380                             mat->AddProperty(&s,AI_MATKEY_TEXTURE_LIGHTMAP(0));
381 
382                             // set the corresponding material flag
383                             matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
384                         }
385                         // alternatively: normal or parallax mapping
386                         else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) {
387                             ++cnt;
388                             s.Set(prop.value);
389                             mat->AddProperty(&s,AI_MATKEY_TEXTURE_NORMALS(0));
390 
391                             // set the corresponding material flag
392                             matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
393                         }
394                         // or just as second diffuse texture
395                         else if (matFlags & AI_IRRMESH_MAT_solid_2layer)    {
396                             ++cnt;
397                             s.Set(prop.value);
398                             mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(1));
399                             ++nd;
400 
401                             // set the corresponding material flag
402                             matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
403                         }
404                         else DefaultLogger::get()->warn("IRRmat: Skipping second texture");
405                     }
406 
407                     else if (prop.name == "Texture3" && cnt == 2)
408                     {
409                         // Irrlicht does not seem to use these channels.
410                         ++cnt;
411                         s.Set(prop.value);
412                         mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+1));
413                     }
414                     else if (prop.name == "Texture4" && cnt == 3)
415                     {
416                         // Irrlicht does not seem to use these channels.
417                         ++cnt;
418                         s.Set(prop.value);
419                         mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+2));
420                     }
421 
422                     // Texture mapping options
423                     if (prop.name == "TextureWrap1" && cnt >= 1)
424                     {
425                         int map = ConvertMappingMode(prop.value);
426                         mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
427                         mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
428                     }
429                     else if (prop.name == "TextureWrap2" && cnt >= 2)
430                     {
431                         int map = ConvertMappingMode(prop.value);
432                         if (matFlags & AI_IRRMESH_MAT_lightmap) {
433                             mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
434                             mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
435                         }
436                         else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
437                             mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
438                             mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
439                         }
440                         else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
441                             mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
442                             mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
443                         }
444                     }
445                     else if (prop.name == "TextureWrap3" && cnt >= 3)
446                     {
447                         int map = ConvertMappingMode(prop.value);
448                         mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+1));
449                         mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+1));
450                     }
451                     else if (prop.name == "TextureWrap4" && cnt >= 4)
452                     {
453                         int map = ConvertMappingMode(prop.value);
454                         mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+2));
455                         mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+2));
456                     }
457                 }
458             }
459             break;
460             case EXN_ELEMENT_END:
461 
462                 /* Assume there are no further nested nodes in <material> elements
463                  */
464                 if (/* IRRMESH */ !ASSIMP_stricmp(reader->getNodeName(),"material") ||
465                     /* IRR     */ !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
466                 {
467                     // Now process lightmapping flags
468                     // We should have at least one textur to do that ..
469                     if (cnt && matFlags & AI_IRRMESH_MAT_lightmap)
470                     {
471                         float f = 1.f;
472                         unsigned int unmasked = matFlags&~AI_IRRMESH_MAT_lightmap;
473 
474                         // Additive lightmap?
475                         int op = (unmasked & AI_IRRMESH_MAT_lightmap_add
476                             ? aiTextureOp_Add : aiTextureOp_Multiply);
477 
478                         // Handle Irrlicht's lightmapping scaling factor
479                         if (unmasked & AI_IRRMESH_MAT_lightmap_m2 ||
480                             unmasked & AI_IRRMESH_MAT_lightmap_light_m2)
481                         {
482                             f = 2.f;
483                         }
484                         else if (unmasked & AI_IRRMESH_MAT_lightmap_m4 ||
485                             unmasked & AI_IRRMESH_MAT_lightmap_light_m4)
486                         {
487                             f = 4.f;
488                         }
489                         mat->AddProperty( &f, 1, AI_MATKEY_TEXBLEND_LIGHTMAP(0));
490                         mat->AddProperty( &op,1, AI_MATKEY_TEXOP_LIGHTMAP(0));
491                     }
492 
493                     return mat;
494                 }
495             default:
496 
497                 // GCC complains here ...
498                 break;
499         }
500     }
501     DefaultLogger::get()->error("IRRMESH: Unexpected end of file. Material is not complete");
502     return mat;
503 }
504 
505 #endif // !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
506