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