1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions 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
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF 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
59using namespace Assimp;
60using namespace irr;
61using namespace irr::io;
62
63// Transformation matrix to convert from Assimp to IRR space
64const 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)
72void 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
90void 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
108void 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
126void 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
145void 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
163void 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
198int 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
212aiMaterial* 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