| /* |
| --------------------------------------------------------------------------- |
| Open Asset Import Library (assimp) |
| --------------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the following |
| conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| --------------------------------------------------------------------------- |
| */ |
| |
| /** @file Implementation of the material oart of the LWO importer class */ |
| |
| |
| |
| #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER |
| |
| // internal headers |
| #include "LWOLoader.h" |
| #include "ByteSwapper.h" |
| |
| |
| using namespace Assimp; |
| |
| // ------------------------------------------------------------------------------------------------ |
| template <class T> |
| T lerp(const T& one, const T& two, float val) |
| { |
| return one + (two-one)*val; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Convert a lightwave mapping mode to our's |
| inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in) |
| { |
| switch (in) |
| { |
| case LWO::Texture::REPEAT: |
| return aiTextureMapMode_Wrap; |
| |
| case LWO::Texture::MIRROR: |
| return aiTextureMapMode_Mirror; |
| |
| case LWO::Texture::RESET: |
| DefaultLogger::get()->warn("LWO2: Unsupported texture map mode: RESET"); |
| |
| // fall though here |
| case LWO::Texture::EDGE: |
| return aiTextureMapMode_Clamp; |
| } |
| return (aiTextureMapMode)0; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| bool LWOImporter::HandleTextures(aiMaterial* pcMat, const TextureList& in, aiTextureType type) |
| { |
| ai_assert(NULL != pcMat); |
| |
| unsigned int cur = 0, temp = 0; |
| aiString s; |
| bool ret = false; |
| |
| for (const auto &texture : in) { |
| if (!texture.enabled || !texture.bCanUse) |
| continue; |
| ret = true; |
| |
| // Convert lightwave's mapping modes to ours. We let them |
| // as they are, the GenUVcoords step will compute UV |
| // channels if they're not there. |
| |
| aiTextureMapping mapping; |
| switch (texture.mapMode) |
| { |
| case LWO::Texture::Planar: |
| mapping = aiTextureMapping_PLANE; |
| break; |
| case LWO::Texture::Cylindrical: |
| mapping = aiTextureMapping_CYLINDER; |
| break; |
| case LWO::Texture::Spherical: |
| mapping = aiTextureMapping_SPHERE; |
| break; |
| case LWO::Texture::Cubic: |
| mapping = aiTextureMapping_BOX; |
| break; |
| case LWO::Texture::FrontProjection: |
| DefaultLogger::get()->error("LWO2: Unsupported texture mapping: FrontProjection"); |
| mapping = aiTextureMapping_OTHER; |
| break; |
| case LWO::Texture::UV: |
| { |
| if( UINT_MAX == texture.mRealUVIndex ) { |
| // We have no UV index for this texture, so we can't display it |
| continue; |
| } |
| |
| // add the UV source index |
| temp = texture.mRealUVIndex; |
| pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_UVWSRC(type,cur)); |
| |
| mapping = aiTextureMapping_UV; |
| } |
| break; |
| default: |
| ai_assert(false); |
| }; |
| |
| if (mapping != aiTextureMapping_UV) { |
| // Setup the main axis |
| aiVector3D v; |
| switch (texture.majorAxis) { |
| case Texture::AXIS_X: |
| v = aiVector3D(1.0,0.0,0.0); |
| break; |
| case Texture::AXIS_Y: |
| v = aiVector3D(0.0,1.0,0.0); |
| break; |
| default: // case Texture::AXIS_Z: |
| v = aiVector3D(0.0,0.0,1.0); |
| break; |
| } |
| |
| pcMat->AddProperty(&v,1,AI_MATKEY_TEXMAP_AXIS(type,cur)); |
| |
| // Setup UV scalings for cylindric and spherical projections |
| if (mapping == aiTextureMapping_CYLINDER || mapping == aiTextureMapping_SPHERE) { |
| aiUVTransform trafo; |
| trafo.mScaling.x = texture.wrapAmountW; |
| trafo.mScaling.y = texture.wrapAmountH; |
| |
| static_assert(sizeof(aiUVTransform)/sizeof(ai_real) == 5, "sizeof(aiUVTransform)/sizeof(ai_real) == 5"); |
| pcMat->AddProperty(&trafo,1,AI_MATKEY_UVTRANSFORM(type,cur)); |
| } |
| DefaultLogger::get()->debug("LWO2: Setting up non-UV mapping"); |
| } |
| |
| // The older LWOB format does not use indirect references to clips. |
| // The file name of a texture is directly specified in the tex chunk. |
| if (mIsLWO2) { |
| // find the corresponding clip (take the last one if multiple |
| // share the same index) |
| ClipList::iterator end = mClips.end(), candidate = end; |
| temp = texture.mClipIdx; |
| for (ClipList::iterator clip = mClips.begin(); clip != end; ++clip) { |
| if ((*clip).idx == temp) { |
| candidate = clip; |
| } |
| |
| } |
| if (candidate == end) { |
| DefaultLogger::get()->error("LWO2: Clip index is out of bounds"); |
| temp = 0; |
| |
| // fixme: apparently some LWO files shipping with Doom3 don't |
| // have clips at all ... check whether that's true or whether |
| // it's a bug in the loader. |
| |
| s.Set("$texture.png"); |
| |
| //continue; |
| } |
| else { |
| if (Clip::UNSUPPORTED == (*candidate).type) { |
| DefaultLogger::get()->error("LWO2: Clip type is not supported"); |
| continue; |
| } |
| AdjustTexturePath((*candidate).path); |
| s.Set((*candidate).path); |
| |
| // Additional image settings |
| int flags = 0; |
| if ((*candidate).negate) { |
| flags |= aiTextureFlags_Invert; |
| } |
| pcMat->AddProperty(&flags,1,AI_MATKEY_TEXFLAGS(type,cur)); |
| } |
| } |
| else |
| { |
| std::string ss = texture.mFileName; |
| if (!ss.length()) { |
| DefaultLogger::get()->error("LWOB: Empty file name"); |
| continue; |
| } |
| AdjustTexturePath(ss); |
| s.Set(ss); |
| } |
| pcMat->AddProperty(&s,AI_MATKEY_TEXTURE(type,cur)); |
| |
| // add the blend factor |
| pcMat->AddProperty<float>(&texture.mStrength,1,AI_MATKEY_TEXBLEND(type,cur)); |
| |
| // add the blend operation |
| switch (texture.blendType) |
| { |
| case LWO::Texture::Normal: |
| case LWO::Texture::Multiply: |
| temp = (unsigned int)aiTextureOp_Multiply; |
| break; |
| |
| case LWO::Texture::Subtractive: |
| case LWO::Texture::Difference: |
| temp = (unsigned int)aiTextureOp_Subtract; |
| break; |
| |
| case LWO::Texture::Divide: |
| temp = (unsigned int)aiTextureOp_Divide; |
| break; |
| |
| case LWO::Texture::Additive: |
| temp = (unsigned int)aiTextureOp_Add; |
| break; |
| |
| default: |
| temp = (unsigned int)aiTextureOp_Multiply; |
| DefaultLogger::get()->warn("LWO2: Unsupported texture blend mode: alpha or displacement"); |
| |
| } |
| // Setup texture operation |
| pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur)); |
| |
| // setup the mapping mode |
| pcMat->AddProperty<int>((int*)&mapping,1,AI_MATKEY_MAPPING(type,cur)); |
| |
| // add the u-wrapping |
| temp = (unsigned int)GetMapMode(texture.wrapModeWidth); |
| pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_U(type,cur)); |
| |
| // add the v-wrapping |
| temp = (unsigned int)GetMapMode(texture.wrapModeHeight); |
| pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_V(type,cur)); |
| |
| ++cur; |
| } |
| return ret; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::ConvertMaterial(const LWO::Surface& surf,aiMaterial* pcMat) |
| { |
| // copy the name of the surface |
| aiString st; |
| st.Set(surf.mName); |
| pcMat->AddProperty(&st,AI_MATKEY_NAME); |
| |
| const int i = surf.bDoubleSided ? 1 : 0; |
| pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED); |
| |
| // add the refraction index and the bump intensity |
| pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI); |
| pcMat->AddProperty(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING); |
| |
| aiShadingMode m; |
| if (surf.mSpecularValue && surf.mGlossiness) |
| { |
| float fGloss; |
| if (mIsLWO2) { |
| fGloss = std::pow( surf.mGlossiness*ai_real( 10.0 )+ ai_real( 2.0 ), ai_real( 2.0 ) ); |
| } |
| else |
| { |
| if (16.0 >= surf.mGlossiness) |
| fGloss = 6.0; |
| else if (64.0 >= surf.mGlossiness) |
| fGloss = 20.0; |
| else if (256.0 >= surf.mGlossiness) |
| fGloss = 50.0; |
| else fGloss = 80.0; |
| } |
| |
| pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH); |
| pcMat->AddProperty(&fGloss,1,AI_MATKEY_SHININESS); |
| m = aiShadingMode_Phong; |
| } |
| else m = aiShadingMode_Gouraud; |
| |
| // specular color |
| aiColor3D clr = lerp( aiColor3D(1.0,1.0,1.0), surf.mColor, surf.mColorHighlights ); |
| pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR); |
| pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH); |
| |
| // emissive color |
| // luminosity is not really the same but it affects the surface in a similar way. Some scaling looks good. |
| clr.g = clr.b = clr.r = surf.mLuminosity*ai_real( 0.8 ); |
| pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE); |
| |
| // opacity ... either additive or default-blended, please |
| if (0.0 != surf.mAdditiveTransparency) { |
| |
| const int add = aiBlendMode_Additive; |
| pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY); |
| pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC); |
| } |
| |
| else if (10e10f != surf.mTransparency) { |
| const int def = aiBlendMode_Default; |
| const float f = 1.0f-surf.mTransparency; |
| pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY); |
| pcMat->AddProperty(&def,1,AI_MATKEY_BLEND_FUNC); |
| } |
| |
| |
| // ADD TEXTURES to the material |
| // TODO: find out how we can handle COLOR textures correctly... |
| bool b = HandleTextures(pcMat,surf.mColorTextures,aiTextureType_DIFFUSE); |
| b = (b || HandleTextures(pcMat,surf.mDiffuseTextures,aiTextureType_DIFFUSE)); |
| HandleTextures(pcMat,surf.mSpecularTextures,aiTextureType_SPECULAR); |
| HandleTextures(pcMat,surf.mGlossinessTextures,aiTextureType_SHININESS); |
| HandleTextures(pcMat,surf.mBumpTextures,aiTextureType_HEIGHT); |
| HandleTextures(pcMat,surf.mOpacityTextures,aiTextureType_OPACITY); |
| HandleTextures(pcMat,surf.mReflectionTextures,aiTextureType_REFLECTION); |
| |
| // Now we need to know which shader to use .. iterate through the shader list of |
| // the surface and search for a name which we know ... |
| for (const auto &shader : surf.mShaders) { |
| if (shader.functionName == "LW_SuperCelShader" || shader.functionName == "AH_CelShader") { |
| DefaultLogger::get()->info("LWO2: Mapping LW_SuperCelShader/AH_CelShader to aiShadingMode_Toon"); |
| |
| m = aiShadingMode_Toon; |
| break; |
| } |
| else if (shader.functionName == "LW_RealFresnel" || shader.functionName == "LW_FastFresnel") { |
| DefaultLogger::get()->info("LWO2: Mapping LW_RealFresnel/LW_FastFresnel to aiShadingMode_Fresnel"); |
| |
| m = aiShadingMode_Fresnel; |
| break; |
| } |
| else |
| { |
| DefaultLogger::get()->warn("LWO2: Unknown surface shader: " + shader.functionName); |
| } |
| } |
| if (surf.mMaximumSmoothAngle <= 0.0) |
| m = aiShadingMode_Flat; |
| pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL); |
| |
| // (the diffuse value is just a scaling factor) |
| // If a diffuse texture is set, we set this value to 1.0 |
| clr = (b && false ? aiColor3D(1.0,1.0,1.0) : surf.mColor); |
| clr.r *= surf.mDiffuseValue; |
| clr.g *= surf.mDiffuseValue; |
| clr.b *= surf.mDiffuseValue; |
| pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| char LWOImporter::FindUVChannels(LWO::TextureList& list, |
| LWO::Layer& /*layer*/,LWO::UVChannel& uv, unsigned int next) |
| { |
| char ret = 0; |
| for (auto &texture : list) { |
| |
| // Ignore textures with non-UV mappings for the moment. |
| if (!texture.enabled || !texture.bCanUse || texture.mapMode != LWO::Texture::UV) { |
| continue; |
| } |
| |
| if (texture.mUVChannelIndex == uv.name) { |
| ret = 1; |
| |
| // got it. |
| if (texture.mRealUVIndex == UINT_MAX || texture.mRealUVIndex == next) |
| { |
| texture.mRealUVIndex = next; |
| } |
| else { |
| // channel mismatch. need to duplicate the material. |
| DefaultLogger::get()->warn("LWO: Channel mismatch, would need to duplicate surface [design bug]"); |
| |
| // TODO |
| } |
| } |
| } |
| return ret; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::FindUVChannels(LWO::Surface& surf, |
| LWO::SortedRep& sorted,LWO::Layer& layer, |
| unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]) |
| { |
| unsigned int next = 0, extra = 0, num_extra = 0; |
| |
| // Check whether we have an UV entry != 0 for one of the faces in 'sorted' |
| for (unsigned int i = 0; i < layer.mUVChannels.size();++i) { |
| LWO::UVChannel& uv = layer.mUVChannels[i]; |
| |
| for (LWO::SortedRep::const_iterator it = sorted.begin(); it != sorted.end(); ++it) { |
| |
| LWO::Face& face = layer.mFaces[*it]; |
| |
| for (unsigned int n = 0; n < face.mNumIndices; ++n) { |
| unsigned int idx = face.mIndices[n]; |
| |
| if (uv.abAssigned[idx] && ((aiVector2D*)&uv.rawData[0])[idx] != aiVector2D()) { |
| |
| if (extra >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { |
| |
| DefaultLogger::get()->error("LWO: Maximum number of UV channels for " |
| "this mesh reached. Skipping channel \'" + uv.name + "\'"); |
| |
| } |
| else { |
| // Search through all textures assigned to 'surf' and look for this UV channel |
| char had = 0; |
| had |= FindUVChannels(surf.mColorTextures,layer,uv,next); |
| had |= FindUVChannels(surf.mDiffuseTextures,layer,uv,next); |
| had |= FindUVChannels(surf.mSpecularTextures,layer,uv,next); |
| had |= FindUVChannels(surf.mGlossinessTextures,layer,uv,next); |
| had |= FindUVChannels(surf.mOpacityTextures,layer,uv,next); |
| had |= FindUVChannels(surf.mBumpTextures,layer,uv,next); |
| had |= FindUVChannels(surf.mReflectionTextures,layer,uv,next); |
| |
| // We have a texture referencing this UV channel so we have to take special care |
| // and are willing to drop unreferenced channels in favour of it. |
| if (had != 0) { |
| if (num_extra) { |
| |
| for (unsigned int a = next; a < std::min( extra, AI_MAX_NUMBER_OF_TEXTURECOORDS-1u ); ++a) { |
| out[a+1] = out[a]; |
| } |
| } |
| ++extra; |
| out[next++] = i; |
| } |
| // Bah ... seems not to be used at all. Push to end if enough space is available. |
| else { |
| out[extra++] = i; |
| ++num_extra; |
| } |
| } |
| it = sorted.end()-1; |
| break; |
| } |
| } |
| } |
| } |
| if (extra < AI_MAX_NUMBER_OF_TEXTURECOORDS) { |
| out[extra] = UINT_MAX; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::FindVCChannels(const LWO::Surface& surf, LWO::SortedRep& sorted, const LWO::Layer& layer, |
| unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]) |
| { |
| unsigned int next = 0; |
| |
| // Check whether we have an vc entry != 0 for one of the faces in 'sorted' |
| for (unsigned int i = 0; i < layer.mVColorChannels.size();++i) { |
| const LWO::VColorChannel& vc = layer.mVColorChannels[i]; |
| |
| if (surf.mVCMap == vc.name) { |
| // The vertex color map is explicitly requested by the surface so we need to take special care of it |
| for (unsigned int a = 0; a < std::min(next,AI_MAX_NUMBER_OF_COLOR_SETS-1u); ++a) { |
| out[a+1] = out[a]; |
| } |
| out[0] = i; |
| ++next; |
| } |
| else { |
| |
| for (LWO::SortedRep::iterator it = sorted.begin(); it != sorted.end(); ++it) { |
| const LWO::Face& face = layer.mFaces[*it]; |
| |
| for (unsigned int n = 0; n < face.mNumIndices; ++n) { |
| unsigned int idx = face.mIndices[n]; |
| |
| if (vc.abAssigned[idx] && ((aiColor4D*)&vc.rawData[0])[idx] != aiColor4D(0.0,0.0,0.0,1.0)) { |
| if (next >= AI_MAX_NUMBER_OF_COLOR_SETS) { |
| |
| DefaultLogger::get()->error("LWO: Maximum number of vertex color channels for " |
| "this mesh reached. Skipping channel \'" + vc.name + "\'"); |
| |
| } |
| else { |
| out[next++] = i; |
| } |
| it = sorted.end()-1; |
| break; |
| } |
| } |
| } |
| } |
| } |
| if (next != AI_MAX_NUMBER_OF_COLOR_SETS) { |
| out[next] = UINT_MAX; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex ) |
| { |
| LE_NCONST uint8_t* const end = mFileBuffer + size; |
| while (true) |
| { |
| if (mFileBuffer + 6 >= end)break; |
| LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer); |
| |
| if (mFileBuffer + head.length > end) |
| throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length"); |
| |
| uint8_t* const next = mFileBuffer+head.length; |
| switch (head.type) |
| { |
| case AI_LWO_PROJ: |
| tex.mapMode = (Texture::MappingMode)GetU2(); |
| break; |
| case AI_LWO_WRAP: |
| tex.wrapModeWidth = (Texture::Wrap)GetU2(); |
| tex.wrapModeHeight = (Texture::Wrap)GetU2(); |
| break; |
| case AI_LWO_AXIS: |
| tex.majorAxis = (Texture::Axes)GetU2(); |
| break; |
| case AI_LWO_IMAG: |
| tex.mClipIdx = GetU2(); |
| break; |
| case AI_LWO_VMAP: |
| GetS0(tex.mUVChannelIndex,head.length); |
| break; |
| case AI_LWO_WRPH: |
| tex.wrapAmountH = GetF4(); |
| break; |
| case AI_LWO_WRPW: |
| tex.wrapAmountW = GetF4(); |
| break; |
| } |
| mFileBuffer = next; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2Procedural(unsigned int /*size*/, LWO::Texture& tex ) |
| { |
| // --- not supported at the moment |
| DefaultLogger::get()->error("LWO2: Found procedural texture, this is not supported"); |
| tex.bCanUse = false; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2Gradient(unsigned int /*size*/, LWO::Texture& tex ) |
| { |
| // --- not supported at the moment |
| DefaultLogger::get()->error("LWO2: Found gradient texture, this is not supported"); |
| tex.bCanUse = false; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex ) |
| { |
| LE_NCONST uint8_t* const end = mFileBuffer + size; |
| |
| // get the ordinal string |
| GetS0( tex.ordinal, size); |
| |
| // we could crash later if this is an empty string ... |
| if (!tex.ordinal.length()) |
| { |
| DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string"); |
| tex.ordinal = "\x00"; |
| } |
| while (true) |
| { |
| if (mFileBuffer + 6 >= end)break; |
| const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer); |
| |
| if (mFileBuffer + head.length > end) |
| throw DeadlyImportError("LWO2: Invalid texture header chunk length"); |
| |
| uint8_t* const next = mFileBuffer+head.length; |
| switch (head.type) |
| { |
| case AI_LWO_CHAN: |
| tex.type = GetU4(); |
| break; |
| case AI_LWO_ENAB: |
| tex.enabled = GetU2() ? true : false; |
| break; |
| case AI_LWO_OPAC: |
| tex.blendType = (Texture::BlendType)GetU2(); |
| tex.mStrength = GetF4(); |
| break; |
| } |
| mFileBuffer = next; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head, unsigned int size ) |
| { |
| ai_assert(!mSurfaces->empty()); |
| LWO::Surface& surf = mSurfaces->back(); |
| LWO::Texture tex; |
| |
| // load the texture header |
| LoadLWO2TextureHeader(head->length,tex); |
| size -= head->length + 6; |
| |
| // now get the exact type of the texture |
| switch (head->type) |
| { |
| case AI_LWO_PROC: |
| LoadLWO2Procedural(size,tex); |
| break; |
| case AI_LWO_GRAD: |
| LoadLWO2Gradient(size,tex); |
| break; |
| case AI_LWO_IMAP: |
| LoadLWO2ImageMap(size,tex); |
| } |
| |
| // get the destination channel |
| TextureList* listRef = NULL; |
| switch (tex.type) |
| { |
| case AI_LWO_COLR: |
| listRef = &surf.mColorTextures;break; |
| case AI_LWO_DIFF: |
| listRef = &surf.mDiffuseTextures;break; |
| case AI_LWO_SPEC: |
| listRef = &surf.mSpecularTextures;break; |
| case AI_LWO_GLOS: |
| listRef = &surf.mGlossinessTextures;break; |
| case AI_LWO_BUMP: |
| listRef = &surf.mBumpTextures;break; |
| case AI_LWO_TRAN: |
| listRef = &surf.mOpacityTextures;break; |
| case AI_LWO_REFL: |
| listRef = &surf.mReflectionTextures;break; |
| default: |
| DefaultLogger::get()->warn("LWO2: Encountered unknown texture type"); |
| return; |
| } |
| |
| // now attach the texture to the parent surface - sort by ordinal string |
| for (TextureList::iterator it = listRef->begin();it != listRef->end(); ++it) { |
| if (::strcmp(tex.ordinal.c_str(),(*it).ordinal.c_str()) < 0) { |
| listRef->insert(it,tex); |
| return; |
| } |
| } |
| listRef->push_back(tex); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* /*head*/, unsigned int size ) |
| { |
| LE_NCONST uint8_t* const end = mFileBuffer + size; |
| |
| ai_assert(!mSurfaces->empty()); |
| LWO::Surface& surf = mSurfaces->back(); |
| LWO::Shader shader; |
| |
| // get the ordinal string |
| GetS0( shader.ordinal, size); |
| |
| // we could crash later if this is an empty string ... |
| if (!shader.ordinal.length()) |
| { |
| DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string"); |
| shader.ordinal = "\x00"; |
| } |
| |
| // read the header |
| while (true) |
| { |
| if (mFileBuffer + 6 >= end)break; |
| const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer); |
| |
| if (mFileBuffer + head.length > end) |
| throw DeadlyImportError("LWO2: Invalid shader header chunk length"); |
| |
| uint8_t* const next = mFileBuffer+head.length; |
| switch (head.type) |
| { |
| case AI_LWO_ENAB: |
| shader.enabled = GetU2() ? true : false; |
| break; |
| |
| case AI_LWO_FUNC: |
| GetS0( shader.functionName, head.length ); |
| } |
| mFileBuffer = next; |
| } |
| |
| // now attach the shader to the parent surface - sort by ordinal string |
| for (ShaderList::iterator it = surf.mShaders.begin();it != surf.mShaders.end(); ++it) { |
| if (::strcmp(shader.ordinal.c_str(),(*it).ordinal.c_str()) < 0) { |
| surf.mShaders.insert(it,shader); |
| return; |
| } |
| } |
| surf.mShaders.push_back(shader); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void LWOImporter::LoadLWO2Surface(unsigned int size) |
| { |
| LE_NCONST uint8_t* const end = mFileBuffer + size; |
| |
| mSurfaces->push_back( LWO::Surface () ); |
| LWO::Surface& surf = mSurfaces->back(); |
| |
| GetS0(surf.mName,size); |
| |
| // check whether this surface was derived from any other surface |
| std::string derived; |
| GetS0(derived,(unsigned int)(end - mFileBuffer)); |
| if (derived.length()) { |
| // yes, find this surface |
| for (SurfaceList::iterator it = mSurfaces->begin(), end = mSurfaces->end()-1; it != end; ++it) { |
| if ((*it).mName == derived) { |
| // we have it ... |
| surf = *it; |
| derived.clear();break; |
| } |
| } |
| if (derived.size()) |
| DefaultLogger::get()->warn("LWO2: Unable to find source surface: " + derived); |
| } |
| |
| while (true) |
| { |
| if (mFileBuffer + 6 >= end) |
| break; |
| const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer); |
| |
| if (mFileBuffer + head.length > end) |
| throw DeadlyImportError("LWO2: Invalid surface chunk length"); |
| |
| uint8_t* const next = mFileBuffer+head.length; |
| switch (head.type) |
| { |
| // diffuse color |
| case AI_LWO_COLR: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,12); |
| surf.mColor.r = GetF4(); |
| surf.mColor.g = GetF4(); |
| surf.mColor.b = GetF4(); |
| break; |
| } |
| // diffuse strength ... hopefully |
| case AI_LWO_DIFF: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,4); |
| surf.mDiffuseValue = GetF4(); |
| break; |
| } |
| // specular strength ... hopefully |
| case AI_LWO_SPEC: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,4); |
| surf.mSpecularValue = GetF4(); |
| break; |
| } |
| // transparency |
| case AI_LWO_TRAN: |
| { |
| // transparency explicitly disabled? |
| if (surf.mTransparency == 10e10f) |
| break; |
| |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,4); |
| surf.mTransparency = GetF4(); |
| break; |
| } |
| // additive transparency |
| case AI_LWO_ADTR: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,ADTR,4); |
| surf.mAdditiveTransparency = GetF4(); |
| break; |
| } |
| // wireframe mode |
| case AI_LWO_LINE: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LINE,2); |
| if (GetU2() & 0x1) |
| surf.mWireframe = true; |
| break; |
| } |
| // glossiness |
| case AI_LWO_GLOS: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,4); |
| surf.mGlossiness = GetF4(); |
| break; |
| } |
| // bump intensity |
| case AI_LWO_BUMP: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,BUMP,4); |
| surf.mBumpIntensity = GetF4(); |
| break; |
| } |
| // color highlights |
| case AI_LWO_CLRH: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,CLRH,4); |
| surf.mColorHighlights = GetF4(); |
| break; |
| } |
| // index of refraction |
| case AI_LWO_RIND: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,RIND,4); |
| surf.mIOR = GetF4(); |
| break; |
| } |
| // polygon sidedness |
| case AI_LWO_SIDE: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SIDE,2); |
| surf.bDoubleSided = (3 == GetU2()); |
| break; |
| } |
| // maximum smoothing angle |
| case AI_LWO_SMAN: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4); |
| surf.mMaximumSmoothAngle = std::fabs( GetF4() ); |
| break; |
| } |
| // vertex color channel to be applied to the surface |
| case AI_LWO_VCOL: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,VCOL,12); |
| surf.mDiffuseValue *= GetF4(); // strength |
| ReadVSizedIntLWO2(mFileBuffer); // skip envelope |
| surf.mVCMapType = GetU4(); // type of the channel |
| |
| // name of the channel |
| GetS0(surf.mVCMap, (unsigned int) (next - mFileBuffer )); |
| break; |
| } |
| // surface bock entry |
| case AI_LWO_BLOK: |
| { |
| AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,BLOK,4); |
| IFF::SubChunkHeader head2 = IFF::LoadSubChunk(mFileBuffer); |
| |
| switch (head2.type) |
| { |
| case AI_LWO_PROC: |
| case AI_LWO_GRAD: |
| case AI_LWO_IMAP: |
| LoadLWO2TextureBlock(&head2, head.length); |
| break; |
| case AI_LWO_SHDR: |
| LoadLWO2ShaderBlock(&head2, head.length); |
| break; |
| |
| default: |
| DefaultLogger::get()->warn("LWO2: Found an unsupported surface BLOK"); |
| }; |
| |
| break; |
| } |
| } |
| mFileBuffer = next; |
| } |
| } |
| |
| #endif // !! ASSIMP_BUILD_NO_X_IMPORTER |