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