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