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) |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |