1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file 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 | |
54 | using namespace Assimp; |
55 | |
56 | // ------------------------------------------------------------------------------------------------ |
57 | template <class T> |
58 | T 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 |
65 | inline 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 | // ------------------------------------------------------------------------------------------------ |
86 | bool 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 | // ------------------------------------------------------------------------------------------------ |
271 | void 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 | // ------------------------------------------------------------------------------------------------ |
379 | char 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 | // ------------------------------------------------------------------------------------------------ |
410 | void 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, = 0, = 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 | // ------------------------------------------------------------------------------------------------ |
476 | void 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 | // ------------------------------------------------------------------------------------------------ |
524 | void 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 | // ------------------------------------------------------------------------------------------------ |
566 | void 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 | // ------------------------------------------------------------------------------------------------ |
574 | void 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 | // ------------------------------------------------------------------------------------------------ |
582 | void LWOImporter::(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 | // ------------------------------------------------------------------------------------------------ |
622 | void LWOImporter::(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 | // ------------------------------------------------------------------------------------------------ |
679 | void LWOImporter::(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 | // ------------------------------------------------------------------------------------------------ |
730 | void 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 | |