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