1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2021, 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 Implementation of the material oart of the LWO importer class */
45 
46 #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
47 
48 // internal headers
49 #include "LWOLoader.h"
50 #include <assimp/ByteSwapper.h>
51 
52 using namespace Assimp;
53 
54 // ------------------------------------------------------------------------------------------------
55 template <class T>
lerp(const T & one,const T & two,float val)56 T lerp(const T &one, const T &two, float val) {
57     return one + (two - one) * val;
58 }
59 
60 // ------------------------------------------------------------------------------------------------
61 // Convert a lightwave mapping mode to our's
GetMapMode(LWO::Texture::Wrap in)62 inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in) {
63     switch (in) {
64         case LWO::Texture::REPEAT:
65             return aiTextureMapMode_Wrap;
66 
67         case LWO::Texture::MIRROR:
68             return aiTextureMapMode_Mirror;
69 
70         case LWO::Texture::RESET:
71             ASSIMP_LOG_WARN("LWO2: Unsupported texture map mode: RESET");
72 
73             // fall though here
74         case LWO::Texture::EDGE:
75             return aiTextureMapMode_Clamp;
76     }
77     return (aiTextureMapMode)0;
78 }
79 
80 // ------------------------------------------------------------------------------------------------
HandleTextures(aiMaterial * pcMat,const TextureList & in,aiTextureType type)81 bool LWOImporter::HandleTextures(aiMaterial *pcMat, const TextureList &in, aiTextureType type) {
82     ai_assert(nullptr != pcMat);
83 
84     unsigned int cur = 0, temp = 0;
85     aiString s;
86     bool ret = false;
87 
88     for (const auto &texture : in) {
89         if (!texture.enabled || !texture.bCanUse)
90             continue;
91         ret = true;
92 
93         // Convert lightwave's mapping modes to ours. We let them
94         // as they are, the GenUVcoords step will compute UV
95         // channels if they're not there.
96 
97         aiTextureMapping mapping = aiTextureMapping_OTHER;
98         switch (texture.mapMode) {
99             case LWO::Texture::Planar:
100                 mapping = aiTextureMapping_PLANE;
101                 break;
102             case LWO::Texture::Cylindrical:
103                 mapping = aiTextureMapping_CYLINDER;
104                 break;
105             case LWO::Texture::Spherical:
106                 mapping = aiTextureMapping_SPHERE;
107                 break;
108             case LWO::Texture::Cubic:
109                 mapping = aiTextureMapping_BOX;
110                 break;
111             case LWO::Texture::FrontProjection:
112                 ASSIMP_LOG_ERROR("LWO2: Unsupported texture mapping: FrontProjection");
113                 mapping = aiTextureMapping_OTHER;
114                 break;
115             case LWO::Texture::UV: {
116                 if (UINT_MAX == texture.mRealUVIndex) {
117                     // We have no UV index for this texture, so we can't display it
118                     continue;
119                 }
120 
121                 // add the UV source index
122                 temp = texture.mRealUVIndex;
123                 pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_UVWSRC(type, cur));
124 
125                 mapping = aiTextureMapping_UV;
126             } break;
127             default:
128                 ai_assert(false);
129         };
130 
131         if (mapping != aiTextureMapping_UV) {
132             // Setup the main axis
133             aiVector3D v;
134             switch (texture.majorAxis) {
135                 case Texture::AXIS_X:
136                     v = aiVector3D(1.0, 0.0, 0.0);
137                     break;
138                 case Texture::AXIS_Y:
139                     v = aiVector3D(0.0, 1.0, 0.0);
140                     break;
141                 default: // case Texture::AXIS_Z:
142                     v = aiVector3D(0.0, 0.0, 1.0);
143                     break;
144             }
145 
146             pcMat->AddProperty(&v, 1, AI_MATKEY_TEXMAP_AXIS(type, cur));
147 
148             // Setup UV scalings for cylindric and spherical projections
149             if (mapping == aiTextureMapping_CYLINDER || mapping == aiTextureMapping_SPHERE) {
150                 aiUVTransform trafo;
151                 trafo.mScaling.x = texture.wrapAmountW;
152                 trafo.mScaling.y = texture.wrapAmountH;
153 
154                 static_assert(sizeof(aiUVTransform) / sizeof(ai_real) == 5, "sizeof(aiUVTransform)/sizeof(ai_real) == 5");
155                 pcMat->AddProperty(&trafo, 1, AI_MATKEY_UVTRANSFORM(type, cur));
156             }
157             ASSIMP_LOG_VERBOSE_DEBUG("LWO2: Setting up non-UV mapping");
158         }
159 
160         // The older LWOB format does not use indirect references to clips.
161         // The file name of a texture is directly specified in the tex chunk.
162         if (mIsLWO2) {
163             // find the corresponding clip (take the last one if multiple
164             // share the same index)
165             ClipList::iterator end = mClips.end(), candidate = end;
166             temp = texture.mClipIdx;
167             for (ClipList::iterator clip = mClips.begin(); clip != end; ++clip) {
168                 if ((*clip).idx == temp) {
169                     candidate = clip;
170                 }
171             }
172             if (candidate == end) {
173                 ASSIMP_LOG_ERROR("LWO2: Clip index is out of bounds");
174                 temp = 0;
175 
176                 // fixme: apparently some LWO files shipping with Doom3 don't
177                 // have clips at all ... check whether that's true or whether
178                 // it's a bug in the loader.
179 
180                 s.Set("$texture.png");
181 
182                 //continue;
183             } else {
184                 if (Clip::UNSUPPORTED == (*candidate).type) {
185                     ASSIMP_LOG_ERROR("LWO2: Clip type is not supported");
186                     continue;
187                 }
188                 AdjustTexturePath((*candidate).path);
189                 s.Set((*candidate).path);
190 
191                 // Additional image settings
192                 int flags = 0;
193                 if ((*candidate).negate) {
194                     flags |= aiTextureFlags_Invert;
195                 }
196                 pcMat->AddProperty(&flags, 1, AI_MATKEY_TEXFLAGS(type, cur));
197             }
198         } else {
199             std::string ss = texture.mFileName;
200             if (!ss.length()) {
201                 ASSIMP_LOG_WARN("LWOB: Empty file name");
202                 continue;
203             }
204             AdjustTexturePath(ss);
205             s.Set(ss);
206         }
207         pcMat->AddProperty(&s, AI_MATKEY_TEXTURE(type, cur));
208 
209         // add the blend factor
210         pcMat->AddProperty<float>(&texture.mStrength, 1, AI_MATKEY_TEXBLEND(type, cur));
211 
212         // add the blend operation
213         switch (texture.blendType) {
214             case LWO::Texture::Normal:
215             case LWO::Texture::Multiply:
216                 temp = (unsigned int)aiTextureOp_Multiply;
217                 break;
218 
219             case LWO::Texture::Subtractive:
220             case LWO::Texture::Difference:
221                 temp = (unsigned int)aiTextureOp_Subtract;
222                 break;
223 
224             case LWO::Texture::Divide:
225                 temp = (unsigned int)aiTextureOp_Divide;
226                 break;
227 
228             case LWO::Texture::Additive:
229                 temp = (unsigned int)aiTextureOp_Add;
230                 break;
231 
232             default:
233                 temp = (unsigned int)aiTextureOp_Multiply;
234                 ASSIMP_LOG_WARN("LWO2: Unsupported texture blend mode: alpha or displacement");
235         }
236         // Setup texture operation
237         pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_TEXOP(type, cur));
238 
239         // setup the mapping mode
240         int mapping_ = static_cast<int>(mapping);
241         pcMat->AddProperty<int>(&mapping_, 1, AI_MATKEY_MAPPING(type, cur));
242 
243         // add the u-wrapping
244         temp = (unsigned int)GetMapMode(texture.wrapModeWidth);
245         pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_MAPPINGMODE_U(type, cur));
246 
247         // add the v-wrapping
248         temp = (unsigned int)GetMapMode(texture.wrapModeHeight);
249         pcMat->AddProperty<int>((int *)&temp, 1, AI_MATKEY_MAPPINGMODE_V(type, cur));
250 
251         ++cur;
252     }
253     return ret;
254 }
255 
256 // ------------------------------------------------------------------------------------------------
ConvertMaterial(const LWO::Surface & surf,aiMaterial * pcMat)257 void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) {
258     // copy the name of the surface
259     aiString st;
260     st.Set(surf.mName);
261     pcMat->AddProperty(&st, AI_MATKEY_NAME);
262 
263     const int i = surf.bDoubleSided ? 1 : 0;
264     pcMat->AddProperty(&i, 1, AI_MATKEY_TWOSIDED);
265 
266     // add the refraction index and the bump intensity
267     pcMat->AddProperty(&surf.mIOR, 1, AI_MATKEY_REFRACTI);
268     pcMat->AddProperty(&surf.mBumpIntensity, 1, AI_MATKEY_BUMPSCALING);
269 
270     aiShadingMode m;
271     if (surf.mSpecularValue && surf.mGlossiness) {
272         float fGloss;
273         if (mIsLWO2) {
274             fGloss = std::pow(surf.mGlossiness * ai_real(10.0) + ai_real(2.0), ai_real(2.0));
275         } else {
276             if (16.0 >= surf.mGlossiness)
277                 fGloss = 6.0;
278             else if (64.0 >= surf.mGlossiness)
279                 fGloss = 20.0;
280             else if (256.0 >= surf.mGlossiness)
281                 fGloss = 50.0;
282             else
283                 fGloss = 80.0;
284         }
285 
286         pcMat->AddProperty(&surf.mSpecularValue, 1, AI_MATKEY_SHININESS_STRENGTH);
287         pcMat->AddProperty(&fGloss, 1, AI_MATKEY_SHININESS);
288         m = aiShadingMode_Phong;
289     } else
290         m = aiShadingMode_Gouraud;
291 
292     // specular color
293     aiColor3D clr = lerp(aiColor3D(1.0, 1.0, 1.0), surf.mColor, surf.mColorHighlights);
294     pcMat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR);
295     pcMat->AddProperty(&surf.mSpecularValue, 1, AI_MATKEY_SHININESS_STRENGTH);
296 
297     // emissive color
298     // luminosity is not really the same but it affects the surface in a similar way. Some scaling looks good.
299     clr.g = clr.b = clr.r = surf.mLuminosity * ai_real(0.8);
300     pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_EMISSIVE);
301 
302     // opacity ... either additive or default-blended, please
303     if (0.0 != surf.mAdditiveTransparency) {
304         const int add = aiBlendMode_Additive;
305         pcMat->AddProperty(&surf.mAdditiveTransparency, 1, AI_MATKEY_OPACITY);
306         pcMat->AddProperty(&add, 1, AI_MATKEY_BLEND_FUNC);
307     } else if (10e10f != surf.mTransparency) {
308         const int def = aiBlendMode_Default;
309         const float f = 1.0f - surf.mTransparency;
310         pcMat->AddProperty(&f, 1, AI_MATKEY_OPACITY);
311         pcMat->AddProperty(&def, 1, AI_MATKEY_BLEND_FUNC);
312     }
313 
314     // ADD TEXTURES to the material
315     // TODO: find out how we can handle COLOR textures correctly...
316     bool b = HandleTextures(pcMat, surf.mColorTextures, aiTextureType_DIFFUSE);
317     b = (b || HandleTextures(pcMat, surf.mDiffuseTextures, aiTextureType_DIFFUSE));
318     HandleTextures(pcMat, surf.mSpecularTextures, aiTextureType_SPECULAR);
319     HandleTextures(pcMat, surf.mGlossinessTextures, aiTextureType_SHININESS);
320     HandleTextures(pcMat, surf.mBumpTextures, aiTextureType_HEIGHT);
321     HandleTextures(pcMat, surf.mOpacityTextures, aiTextureType_OPACITY);
322     HandleTextures(pcMat, surf.mReflectionTextures, aiTextureType_REFLECTION);
323 
324     // Now we need to know which shader to use .. iterate through the shader list of
325     // the surface and  search for a name which we know ...
326     for (const auto &shader : surf.mShaders) {
327         if (shader.functionName == "LW_SuperCelShader" || shader.functionName == "AH_CelShader") {
328             ASSIMP_LOG_INFO("LWO2: Mapping LW_SuperCelShader/AH_CelShader to aiShadingMode_Toon");
329 
330             m = aiShadingMode_Toon;
331             break;
332         } else if (shader.functionName == "LW_RealFresnel" || shader.functionName == "LW_FastFresnel") {
333             ASSIMP_LOG_INFO("LWO2: Mapping LW_RealFresnel/LW_FastFresnel to aiShadingMode_Fresnel");
334 
335             m = aiShadingMode_Fresnel;
336             break;
337         } else {
338             ASSIMP_LOG_WARN("LWO2: Unknown surface shader: ", shader.functionName);
339         }
340     }
341     if (surf.mMaximumSmoothAngle <= 0.0)
342         m = aiShadingMode_Flat;
343     int m_ = static_cast<int>(m);
344     pcMat->AddProperty(&m_, 1, AI_MATKEY_SHADING_MODEL);
345 
346     // (the diffuse value is just a scaling factor)
347     // If a diffuse texture is set, we set this value to 1.0
348     clr = (b && false ? aiColor3D(1.0, 1.0, 1.0) : surf.mColor);
349     clr.r *= surf.mDiffuseValue;
350     clr.g *= surf.mDiffuseValue;
351     clr.b *= surf.mDiffuseValue;
352     pcMat->AddProperty<aiColor3D>(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
353 }
354 
355 // ------------------------------------------------------------------------------------------------
FindUVChannels(LWO::TextureList & list,LWO::Layer &,LWO::UVChannel & uv,unsigned int next)356 char LWOImporter::FindUVChannels(LWO::TextureList &list,
357         LWO::Layer & /*layer*/, LWO::UVChannel &uv, unsigned int next) {
358     char ret = 0;
359     for (auto &texture : list) {
360 
361         // Ignore textures with non-UV mappings for the moment.
362         if (!texture.enabled || !texture.bCanUse || texture.mapMode != LWO::Texture::UV) {
363             continue;
364         }
365 
366         if (texture.mUVChannelIndex == uv.name) {
367             ret = 1;
368 
369             // got it.
370             if (texture.mRealUVIndex == UINT_MAX || texture.mRealUVIndex == next) {
371                 texture.mRealUVIndex = next;
372             } else {
373                 // channel mismatch. need to duplicate the material.
374                 ASSIMP_LOG_WARN("LWO: Channel mismatch, would need to duplicate surface [design bug]");
375 
376                 // TODO
377             }
378         }
379     }
380     return ret;
381 }
382 
383 // ------------------------------------------------------------------------------------------------
FindUVChannels(LWO::Surface & surf,LWO::SortedRep & sorted,LWO::Layer & layer,unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS])384 void LWOImporter::FindUVChannels(LWO::Surface &surf,
385         LWO::SortedRep &sorted, LWO::Layer &layer,
386         unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]) {
387     unsigned int next = 0, extra = 0, num_extra = 0;
388 
389     // Check whether we have an UV entry != 0 for one of the faces in 'sorted'
390     for (unsigned int i = 0; i < layer.mUVChannels.size(); ++i) {
391         LWO::UVChannel &uv = layer.mUVChannels[i];
392 
393         for (LWO::SortedRep::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
394 
395             LWO::Face &face = layer.mFaces[*it];
396 
397             for (unsigned int n = 0; n < face.mNumIndices; ++n) {
398                 unsigned int idx = face.mIndices[n];
399 
400                 if (uv.abAssigned[idx] && ((aiVector2D *)&uv.rawData[0])[idx] != aiVector2D()) {
401 
402                     if (extra >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
403 
404                         ASSIMP_LOG_ERROR("LWO: Maximum number of UV channels for "
405                                          "this mesh reached. Skipping channel \'" +
406                                          uv.name + "\'");
407 
408                     } else {
409                         // Search through all textures assigned to 'surf' and look for this UV channel
410                         char had = 0;
411                         had |= FindUVChannels(surf.mColorTextures, layer, uv, next);
412                         had |= FindUVChannels(surf.mDiffuseTextures, layer, uv, next);
413                         had |= FindUVChannels(surf.mSpecularTextures, layer, uv, next);
414                         had |= FindUVChannels(surf.mGlossinessTextures, layer, uv, next);
415                         had |= FindUVChannels(surf.mOpacityTextures, layer, uv, next);
416                         had |= FindUVChannels(surf.mBumpTextures, layer, uv, next);
417                         had |= FindUVChannels(surf.mReflectionTextures, layer, uv, next);
418 
419                         // We have a texture referencing this UV channel so we have to take special care
420                         // and are willing to drop unreferenced channels in favour of it.
421                         if (had != 0) {
422                             if (num_extra) {
423 
424                                 for (unsigned int a = next; a < std::min(extra, AI_MAX_NUMBER_OF_TEXTURECOORDS - 1u); ++a) {
425                                     out[a + 1] = out[a];
426                                 }
427                             }
428                             ++extra;
429                             out[next++] = i;
430                         }
431                         // Bah ... seems not to be used at all. Push to end if enough space is available.
432                         else {
433                             out[extra++] = i;
434                             ++num_extra;
435                         }
436                     }
437                     it = sorted.end() - 1;
438                     break;
439                 }
440             }
441         }
442     }
443     if (extra < AI_MAX_NUMBER_OF_TEXTURECOORDS) {
444         out[extra] = UINT_MAX;
445     }
446 }
447 
448 // ------------------------------------------------------------------------------------------------
FindVCChannels(const LWO::Surface & surf,LWO::SortedRep & sorted,const LWO::Layer & layer,unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS])449 void LWOImporter::FindVCChannels(const LWO::Surface &surf, LWO::SortedRep &sorted, const LWO::Layer &layer,
450         unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]) {
451     unsigned int next = 0;
452 
453     // Check whether we have an vc entry != 0 for one of the faces in 'sorted'
454     for (unsigned int i = 0; i < layer.mVColorChannels.size(); ++i) {
455         const LWO::VColorChannel &vc = layer.mVColorChannels[i];
456 
457         if (surf.mVCMap == vc.name) {
458             // The vertex color map is explicitly requested by the surface so we need to take special care of it
459             for (unsigned int a = 0; a < std::min(next, AI_MAX_NUMBER_OF_COLOR_SETS - 1u); ++a) {
460                 out[a + 1] = out[a];
461             }
462             out[0] = i;
463             ++next;
464         } else {
465 
466             for (LWO::SortedRep::iterator it = sorted.begin(); it != sorted.end(); ++it) {
467                 const LWO::Face &face = layer.mFaces[*it];
468 
469                 for (unsigned int n = 0; n < face.mNumIndices; ++n) {
470                     unsigned int idx = face.mIndices[n];
471 
472                     if (vc.abAssigned[idx] && ((aiColor4D *)&vc.rawData[0])[idx] != aiColor4D(0.0, 0.0, 0.0, 1.0)) {
473                         if (next >= AI_MAX_NUMBER_OF_COLOR_SETS) {
474 
475                             ASSIMP_LOG_ERROR("LWO: Maximum number of vertex color channels for "
476                                              "this mesh reached. Skipping channel \'" +
477                                              vc.name + "\'");
478 
479                         } else {
480                             out[next++] = i;
481                         }
482                         it = sorted.end() - 1;
483                         break;
484                     }
485                 }
486             }
487         }
488     }
489     if (next != AI_MAX_NUMBER_OF_COLOR_SETS) {
490         out[next] = UINT_MAX;
491     }
492 }
493 
494 // ------------------------------------------------------------------------------------------------
LoadLWO2ImageMap(unsigned int size,LWO::Texture & tex)495 void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture &tex) {
496     LE_NCONST uint8_t *const end = mFileBuffer + size;
497     while (true) {
498         if (mFileBuffer + 6 >= end) break;
499         LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
500 
501         if (mFileBuffer + head.length > end)
502             throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length");
503 
504         uint8_t *const next = mFileBuffer + head.length;
505         switch (head.type) {
506             case AI_LWO_PROJ:
507                 tex.mapMode = (Texture::MappingMode)GetU2();
508                 break;
509             case AI_LWO_WRAP:
510                 tex.wrapModeWidth = (Texture::Wrap)GetU2();
511                 tex.wrapModeHeight = (Texture::Wrap)GetU2();
512                 break;
513             case AI_LWO_AXIS:
514                 tex.majorAxis = (Texture::Axes)GetU2();
515                 break;
516             case AI_LWO_IMAG:
517                 tex.mClipIdx = GetU2();
518                 break;
519             case AI_LWO_VMAP:
520                 GetS0(tex.mUVChannelIndex, head.length);
521                 break;
522             case AI_LWO_WRPH:
523                 tex.wrapAmountH = GetF4();
524                 break;
525             case AI_LWO_WRPW:
526                 tex.wrapAmountW = GetF4();
527                 break;
528         }
529         mFileBuffer = next;
530     }
531 }
532 
533 // ------------------------------------------------------------------------------------------------
LoadLWO2Procedural(unsigned int,LWO::Texture & tex)534 void LWOImporter::LoadLWO2Procedural(unsigned int /*size*/, LWO::Texture &tex) {
535     // --- not supported at the moment
536     ASSIMP_LOG_ERROR("LWO2: Found procedural texture, this is not supported");
537     tex.bCanUse = false;
538 }
539 
540 // ------------------------------------------------------------------------------------------------
LoadLWO2Gradient(unsigned int,LWO::Texture & tex)541 void LWOImporter::LoadLWO2Gradient(unsigned int /*size*/, LWO::Texture &tex) {
542     // --- not supported at the moment
543     ASSIMP_LOG_ERROR("LWO2: Found gradient texture, this is not supported");
544     tex.bCanUse = false;
545 }
546 
547 // ------------------------------------------------------------------------------------------------
LoadLWO2TextureHeader(unsigned int size,LWO::Texture & tex)548 void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture &tex) {
549     LE_NCONST uint8_t *const end = mFileBuffer + size;
550 
551     // get the ordinal string
552     GetS0(tex.ordinal, size);
553 
554     // we could crash later if this is an empty string ...
555     if (!tex.ordinal.length()) {
556         ASSIMP_LOG_ERROR("LWO2: Ill-formed SURF.BLOK ordinal string");
557         tex.ordinal = "\x00";
558     }
559     while (true) {
560         if (mFileBuffer + 6 >= end) break;
561         const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
562 
563         if (mFileBuffer + head.length > end)
564             throw DeadlyImportError("LWO2: Invalid texture header chunk length");
565 
566         uint8_t *const next = mFileBuffer + head.length;
567         switch (head.type) {
568             case AI_LWO_CHAN:
569                 tex.type = GetU4();
570                 break;
571             case AI_LWO_ENAB:
572                 tex.enabled = GetU2() ? true : false;
573                 break;
574             case AI_LWO_OPAC:
575                 tex.blendType = (Texture::BlendType)GetU2();
576                 tex.mStrength = GetF4();
577                 break;
578         }
579         mFileBuffer = next;
580     }
581 }
582 
583 // ------------------------------------------------------------------------------------------------
LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader * head,unsigned int size)584 void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader *head, unsigned int size) {
585     ai_assert(!mSurfaces->empty());
586     LWO::Surface &surf = mSurfaces->back();
587     LWO::Texture tex;
588 
589     // load the texture header
590     LoadLWO2TextureHeader(head->length, tex);
591     size -= head->length + 6;
592 
593     // now get the exact type of the texture
594     switch (head->type) {
595         case AI_LWO_PROC:
596             LoadLWO2Procedural(size, tex);
597             break;
598         case AI_LWO_GRAD:
599             LoadLWO2Gradient(size, tex);
600             break;
601         case AI_LWO_IMAP:
602             LoadLWO2ImageMap(size, tex);
603     }
604 
605     // get the destination channel
606     TextureList *listRef = nullptr;
607     switch (tex.type) {
608         case AI_LWO_COLR:
609             listRef = &surf.mColorTextures;
610             break;
611         case AI_LWO_DIFF:
612             listRef = &surf.mDiffuseTextures;
613             break;
614         case AI_LWO_SPEC:
615             listRef = &surf.mSpecularTextures;
616             break;
617         case AI_LWO_GLOS:
618             listRef = &surf.mGlossinessTextures;
619             break;
620         case AI_LWO_BUMP:
621             listRef = &surf.mBumpTextures;
622             break;
623         case AI_LWO_TRAN:
624             listRef = &surf.mOpacityTextures;
625             break;
626         case AI_LWO_REFL:
627             listRef = &surf.mReflectionTextures;
628             break;
629         default:
630             ASSIMP_LOG_WARN("LWO2: Encountered unknown texture type");
631             return;
632     }
633 
634     // now attach the texture to the parent surface - sort by ordinal string
635     for (TextureList::iterator it = listRef->begin(); it != listRef->end(); ++it) {
636         if (::strcmp(tex.ordinal.c_str(), (*it).ordinal.c_str()) < 0) {
637             listRef->insert(it, tex);
638             return;
639         }
640     }
641     listRef->push_back(tex);
642 }
643 
644 // ------------------------------------------------------------------------------------------------
LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader *,unsigned int size)645 void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader * /*head*/, unsigned int size) {
646     LE_NCONST uint8_t *const end = mFileBuffer + size;
647 
648     ai_assert(!mSurfaces->empty());
649     LWO::Surface &surf = mSurfaces->back();
650     LWO::Shader shader;
651 
652     // get the ordinal string
653     GetS0(shader.ordinal, size);
654 
655     // we could crash later if this is an empty string ...
656     if (!shader.ordinal.length()) {
657         ASSIMP_LOG_ERROR("LWO2: Ill-formed SURF.BLOK ordinal string");
658         shader.ordinal = "\x00";
659     }
660 
661     // read the header
662     while (true) {
663         if (mFileBuffer + 6 >= end) break;
664         const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
665 
666         if (mFileBuffer + head.length > end)
667             throw DeadlyImportError("LWO2: Invalid shader header chunk length");
668 
669         uint8_t *const next = mFileBuffer + head.length;
670         switch (head.type) {
671             case AI_LWO_ENAB:
672                 shader.enabled = GetU2() ? true : false;
673                 break;
674 
675             case AI_LWO_FUNC:
676                 GetS0(shader.functionName, head.length);
677         }
678         mFileBuffer = next;
679     }
680 
681     // now attach the shader to the parent surface - sort by ordinal string
682     for (ShaderList::iterator it = surf.mShaders.begin(); it != surf.mShaders.end(); ++it) {
683         if (::strcmp(shader.ordinal.c_str(), (*it).ordinal.c_str()) < 0) {
684             surf.mShaders.insert(it, shader);
685             return;
686         }
687     }
688     surf.mShaders.push_back(shader);
689 }
690 
691 // ------------------------------------------------------------------------------------------------
LoadLWO2Surface(unsigned int size)692 void LWOImporter::LoadLWO2Surface(unsigned int size) {
693     LE_NCONST uint8_t *const end = mFileBuffer + size;
694 
695     mSurfaces->push_back(LWO::Surface());
696     LWO::Surface &surf = mSurfaces->back();
697 
698     GetS0(surf.mName, size);
699 
700     // check whether this surface was derived from any other surface
701     std::string derived;
702     GetS0(derived, (unsigned int)(end - mFileBuffer));
703     if (derived.length()) {
704         // yes, find this surface
705         for (SurfaceList::iterator it = mSurfaces->begin(), itEnd = mSurfaces->end() - 1; it != itEnd; ++it) {
706             if ((*it).mName == derived) {
707                 // we have it ...
708                 surf = *it;
709                 derived.clear();
710                 break;
711             }
712         }
713         if (derived.size()) {
714             ASSIMP_LOG_WARN("LWO2: Unable to find source surface: ", derived);
715         }
716     }
717 
718     while (true) {
719         if (mFileBuffer + 6 >= end)
720             break;
721         const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
722 
723         if (mFileBuffer + head.length > end)
724             throw DeadlyImportError("LWO2: Invalid surface chunk length");
725 
726         uint8_t *const next = mFileBuffer + head.length;
727         switch (head.type) {
728                 // diffuse color
729             case AI_LWO_COLR: {
730                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, COLR, 12);
731                 surf.mColor.r = GetF4();
732                 surf.mColor.g = GetF4();
733                 surf.mColor.b = GetF4();
734                 break;
735             }
736                 // diffuse strength ... hopefully
737             case AI_LWO_DIFF: {
738                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, DIFF, 4);
739                 surf.mDiffuseValue = GetF4();
740                 break;
741             }
742                 // specular strength ... hopefully
743             case AI_LWO_SPEC: {
744                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SPEC, 4);
745                 surf.mSpecularValue = GetF4();
746                 break;
747             }
748                 // transparency
749             case AI_LWO_TRAN: {
750                 // transparency explicitly disabled?
751                 if (surf.mTransparency == 10e10f)
752                     break;
753 
754                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, TRAN, 4);
755                 surf.mTransparency = GetF4();
756                 break;
757             }
758                 // additive transparency
759             case AI_LWO_ADTR: {
760                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, ADTR, 4);
761                 surf.mAdditiveTransparency = GetF4();
762                 break;
763             }
764                 // wireframe mode
765             case AI_LWO_LINE: {
766                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, LINE, 2);
767                 if (GetU2() & 0x1)
768                     surf.mWireframe = true;
769                 break;
770             }
771                 // glossiness
772             case AI_LWO_GLOS: {
773                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, GLOS, 4);
774                 surf.mGlossiness = GetF4();
775                 break;
776             }
777                 // bump intensity
778             case AI_LWO_BUMP: {
779                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, BUMP, 4);
780                 surf.mBumpIntensity = GetF4();
781                 break;
782             }
783                 // color highlights
784             case AI_LWO_CLRH: {
785                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, CLRH, 4);
786                 surf.mColorHighlights = GetF4();
787                 break;
788             }
789                 // index of refraction
790             case AI_LWO_RIND: {
791                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, RIND, 4);
792                 surf.mIOR = GetF4();
793                 break;
794             }
795                 // polygon sidedness
796             case AI_LWO_SIDE: {
797                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SIDE, 2);
798                 surf.bDoubleSided = (3 == GetU2());
799                 break;
800             }
801                 // maximum smoothing angle
802             case AI_LWO_SMAN: {
803                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, SMAN, 4);
804                 surf.mMaximumSmoothAngle = std::fabs(GetF4());
805                 break;
806             }
807                 // vertex color channel to be applied to the surface
808             case AI_LWO_VCOL: {
809                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, VCOL, 12);
810                 surf.mDiffuseValue *= GetF4(); // strength
811                 ReadVSizedIntLWO2(mFileBuffer); // skip envelope
812                 surf.mVCMapType = GetU4(); // type of the channel
813 
814                 // name of the channel
815                 GetS0(surf.mVCMap, (unsigned int)(next - mFileBuffer));
816                 break;
817             }
818                 // surface bock entry
819             case AI_LWO_BLOK: {
820                 AI_LWO_VALIDATE_CHUNK_LENGTH(head.length, BLOK, 4);
821                 IFF::SubChunkHeader head2 = IFF::LoadSubChunk(mFileBuffer);
822 
823                 switch (head2.type) {
824                     case AI_LWO_PROC:
825                     case AI_LWO_GRAD:
826                     case AI_LWO_IMAP:
827                         LoadLWO2TextureBlock(&head2, head.length);
828                         break;
829                     case AI_LWO_SHDR:
830                         LoadLWO2ShaderBlock(&head2, head.length);
831                         break;
832 
833                     default:
834                         ASSIMP_LOG_WARN("LWO2: Found an unsupported surface BLOK");
835                 };
836 
837                 break;
838             }
839         }
840         mFileBuffer = next;
841     }
842 }
843 
844 #endif // !! ASSIMP_BUILD_NO_X_IMPORTER
845