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